Files
kleiax 904d14b64c
CI / test (push) Canceled after 0s
Initial commit
2026-09-12 22:22:17 +02:00

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)
}
}