package cabana_test import ( "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" "git.golem15.com/golem15/summercms/modules/cabana" "git.golem15.com/golem15/summercms/modules/lagoon" ) func fileList(t *testing.T, rec *httptest.ResponseRecorder) []cabana.FileItem { t.Helper() if rec.Code != http.StatusOK { t.Fatalf("file list status=%d body=%s", rec.Code, rec.Body.String()) } var body cabana.Envelope[[]cabana.FileItem] if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { t.Fatalf("file list: %v\n%s", err, rec.Body.String()) } return body.Data } func (e *conformEnv) listFiles(t *testing.T, id uint, field, key string) []cabana.FileItem { t.Helper() headers := map[string]string{} if key != "" { headers[cabana.SessionKeyHeader] = key } return fileList(t, e.sendWith(t, http.MethodGet, fmt.Sprintf("/acme/conform/gadgets/%d/files/%s", id, field), nil, "", headers)) } func (e *conformEnv) loginAs(t *testing.T, login string) { t.Helper() e.login = login if rec := e.send(t, http.MethodPost, "/auth/login", map[string]string{"login": login, "password": adminTestPassword}, false); rec.Code != http.StatusOK { t.Fatalf("login %s status=%d body=%s", login, rec.Code, rec.Body.String()) } } func (e *conformEnv) bindingCount(t *testing.T, key string) int64 { t.Helper() var n int64 if err := e.db.Model(&lagoon.DeferredBinding{}).Where("session_key = ?", key).Count(&n).Error; err != nil { t.Fatal(err) } return n } // TestFileuploadSmokeCreateCommit uploads an image to the create form (id 0) // and saves the record with the same session key: the file is attached to // the new record and the binding is gone. func TestFileuploadSmokeCreateCommit(t *testing.T) { env := newConformEnv(t) env.loginAs(t, env.login) key := newSessionKey(t) up := env.upload(t, 0, "photos", "photo.png", conformPNG(t), key) if up.Code != http.StatusCreated { t.Fatalf("upload status=%d body=%s", up.Code, up.Body.String()) } if got := env.listFiles(t, 0, "photos", key); len(got) != 1 || !got[0].Pending { t.Fatalf("pending list = %#v", got) } payload, _ := json.Marshal(map[string]any{"name": "smoke-" + env.stamp}) created := env.sendWith(t, http.MethodPost, "/acme/conform/gadgets", payload, "application/json", map[string]string{cabana.SessionKeyHeader: key}) if created.Code != http.StatusCreated { t.Fatalf("create status=%d body=%s", created.Code, created.Body.String()) } id := dataID(t, created.Body.Bytes()) got := env.listFiles(t, id, "photos", "") if len(got) != 1 || got[0].Pending || got[0].FileName != "photo.png" || got[0].URL == "" { t.Fatalf("attached list = %#v", got) } if n := env.bindingCount(t, key); n != 0 { t.Fatalf("deferred_bindings rows for the key after save = %d", n) } // Without the key, the unsaved form shows nothing: id 0 is 404. if rec := env.sendWith(t, http.MethodGet, "/acme/conform/gadgets/0/files/photos", nil, "", nil); rec.Code != http.StatusNotFound { t.Fatalf("id 0 without key status=%d", rec.Code) } // A malformed key is a 422 on session_key. if rec := env.sendWith(t, http.MethodGet, "/acme/conform/gadgets/0/files/photos", nil, "", map[string]string{cabana.SessionKeyHeader: "short"}); rec.Code != http.StatusUnprocessableEntity { t.Fatalf("malformed key status=%d", rec.Code) } } // TestFileuploadSmokeForeignAdmin replays one admin's session key as another // admin: the pending upload is neither listed nor committed. func TestFileuploadSmokeForeignAdmin(t *testing.T) { env := newConformEnv(t) env.loginAs(t, env.login) key := newSessionKey(t) if up := env.upload(t, 0, "photos", "photo.png", conformPNG(t), key); up.Code != http.StatusCreated { t.Fatalf("upload status=%d body=%s", up.Code, up.Body.String()) } other := *env login := "other-" + env.stamp insertAdmin(t, env.db, login, login+"@example.test", adminTestPassword, true, false) other.loginAs(t, login) if got := other.listFiles(t, 0, "photos", key); len(got) != 0 { t.Fatalf("foreign admin sees %#v", got) } payload, _ := json.Marshal(map[string]any{"name": "foreign-" + env.stamp}) created := other.sendWith(t, http.MethodPost, "/acme/conform/gadgets", payload, "application/json", map[string]string{cabana.SessionKeyHeader: key}) if created.Code != http.StatusCreated { t.Fatalf("create status=%d body=%s", created.Code, created.Body.String()) } if got := other.listFiles(t, dataID(t, created.Body.Bytes()), "photos", ""); len(got) != 0 { t.Fatalf("foreign save attached %#v", got) } if n := env.bindingCount(t, key); n != 1 { t.Fatalf("owner's binding rows = %d, want 1", n) } }