130 lines
5.2 KiB
Go
130 lines
5.2 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"gardomatic.kleiax.de/internal/storage"
|
|
"github.com/julienschmidt/httprouter"
|
|
)
|
|
|
|
type journalTestModel struct {
|
|
items map[int]storage.JournalEntry
|
|
nextID int
|
|
lastListType storage.JournalEntryType
|
|
}
|
|
|
|
func (m *journalTestModel) Insert(entry storage.JournalEntry) (storage.JournalEntry, error) {
|
|
m.nextID++
|
|
entry.ID, entry.Version = m.nextID, 1
|
|
m.items[entry.ID] = entry
|
|
return entry, nil
|
|
}
|
|
|
|
func (m *journalTestModel) Get(gardenID, id int) (storage.JournalEntry, error) {
|
|
entry, ok := m.items[id]
|
|
if !ok || entry.GardenID != gardenID {
|
|
return storage.JournalEntry{}, storage.ErrRecordNotFound
|
|
}
|
|
return entry, nil
|
|
}
|
|
|
|
func (m *journalTestModel) GetAllForGarden(gardenID int, entryType storage.JournalEntryType) ([]storage.JournalEntry, error) {
|
|
m.lastListType = entryType
|
|
entries := []storage.JournalEntry{}
|
|
for _, entry := range m.items {
|
|
if entry.GardenID == gardenID && entry.EntryType == entryType {
|
|
entries = append(entries, entry)
|
|
}
|
|
}
|
|
return entries, nil
|
|
}
|
|
|
|
func (m *journalTestModel) Update(gardenID int, entry storage.JournalEntry) (storage.JournalEntry, error) {
|
|
if _, err := m.Get(gardenID, entry.ID); err != nil {
|
|
return storage.JournalEntry{}, err
|
|
}
|
|
entry.Version++
|
|
m.items[entry.ID] = entry
|
|
return entry, nil
|
|
}
|
|
|
|
func (m *journalTestModel) Delete(gardenID, id int) error {
|
|
if _, err := m.Get(gardenID, id); err != nil {
|
|
return err
|
|
}
|
|
delete(m.items, id)
|
|
return nil
|
|
}
|
|
|
|
func (m *journalTestModel) InsertAttachment(int, int, storage.JournalAttachment) (storage.JournalAttachment, error) {
|
|
return storage.JournalAttachment{}, nil
|
|
}
|
|
func (m *journalTestModel) GetAttachment(int, int, int) (storage.JournalAttachment, error) {
|
|
return storage.JournalAttachment{}, storage.ErrRecordNotFound
|
|
}
|
|
func (m *journalTestModel) DeleteAttachment(int, int, int) error { return nil }
|
|
|
|
type journalTagTestModel struct{}
|
|
|
|
func (journalTagTestModel) Get(int, storage.TagEntity, int) ([]string, error) { return nil, nil }
|
|
func (journalTagTestModel) Set(_ int, _ storage.TagEntity, _ int, tags []string) ([]string, error) {
|
|
return tags, nil
|
|
}
|
|
func (journalTagTestModel) GetAllForGarden(int) ([]string, error) { return nil, nil }
|
|
|
|
func serveJournalRequest(app *application, user storage.User, method, path string, body []byte) *httptest.ResponseRecorder {
|
|
router := httprouter.New()
|
|
protect := func(handler http.HandlerFunc) http.HandlerFunc {
|
|
return app.requireActivatedUser(app.requireGardenMember(handler))
|
|
}
|
|
router.HandlerFunc(http.MethodPost, "/v1/gardens/:gardenID/journal", protect(app.createJournalEntryHandler))
|
|
router.HandlerFunc(http.MethodGet, "/v1/gardens/:gardenID/journal", protect(app.listJournalEntriesHandler))
|
|
request := httptest.NewRequest(method, path, bytes.NewReader(body))
|
|
request.Header.Set("Content-Type", "application/json")
|
|
response := httptest.NewRecorder()
|
|
router.ServeHTTP(response, app.contextSetAuthenticatedUser(request, user))
|
|
return response
|
|
}
|
|
|
|
func TestJournalAPISeparatesPinboardEntries(t *testing.T) {
|
|
app, _, members := newGardenTestApplication()
|
|
user := storage.User{ID: 12, Name: "Ada", Activated: true}
|
|
members.members[[2]int{3, user.ID}] = storage.GardenMember{GardenID: 3, UserID: user.ID, Role: storage.GardenRoleMember}
|
|
model := &journalTestModel{items: map[int]storage.JournalEntry{
|
|
1: {ID: 1, GardenID: 3, EntryType: storage.JournalEntryTypeJournal, Title: "Ernte"},
|
|
2: {ID: 2, GardenID: 3, EntryType: storage.JournalEntryTypePinboard, Title: "Sitzecke"},
|
|
}, nextID: 2}
|
|
app.models.Journal = model
|
|
app.models.Tags = journalTagTestModel{}
|
|
|
|
listed := serveJournalRequest(app, user, http.MethodGet, "/v1/gardens/3/journal?type=pinboard", nil)
|
|
if listed.Code != http.StatusOK || model.lastListType != storage.JournalEntryTypePinboard {
|
|
t.Fatalf("list pinboard: status=%d type=%q body=%s", listed.Code, model.lastListType, listed.Body.String())
|
|
}
|
|
if body := listed.Body.String(); !bytes.Contains([]byte(body), []byte("Sitzecke")) || bytes.Contains([]byte(body), []byte("Ernte")) {
|
|
t.Fatalf("list mixes entry types: %s", body)
|
|
}
|
|
|
|
created := serveJournalRequest(app, user, http.MethodPost, "/v1/gardens/3/journal", []byte(`{"title":"Teichidee","entry_type":"pinboard"}`))
|
|
if created.Code != http.StatusCreated || model.items[3].EntryType != storage.JournalEntryTypePinboard {
|
|
t.Fatalf("create pinboard: status=%d entry=%+v body=%s", created.Code, model.items[3], created.Body.String())
|
|
}
|
|
|
|
imageOnly := serveJournalRequest(app, user, http.MethodPost, "/v1/gardens/3/journal", []byte(`{"entry_type":"pinboard"}`))
|
|
if imageOnly.Code != http.StatusCreated || model.items[4].Title != "" {
|
|
t.Fatalf("create titleless pinboard entry: status=%d entry=%+v body=%s", imageOnly.Code, model.items[4], imageOnly.Body.String())
|
|
}
|
|
untitledJournal := serveJournalRequest(app, user, http.MethodPost, "/v1/gardens/3/journal", []byte(`{"entry_type":"journal"}`))
|
|
if untitledJournal.Code != http.StatusUnprocessableEntity {
|
|
t.Fatalf("untitled journal status: got %d, want %d", untitledJournal.Code, http.StatusUnprocessableEntity)
|
|
}
|
|
|
|
invalid := serveJournalRequest(app, user, http.MethodGet, "/v1/gardens/3/journal?type=unknown", nil)
|
|
if invalid.Code != http.StatusBadRequest {
|
|
t.Fatalf("invalid type: got %d, want %d", invalid.Code, http.StatusBadRequest)
|
|
}
|
|
}
|