package api import ( "palette/internal/store" "palette/internal/web" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" ) // doReq performs a request against the router, carrying the given cookies, // and returns the recorder (so Set-Cookie from the viewer middleware is visible). func doReq(t *testing.T, h http.Handler, method, path, cookie string, body string) *httptest.ResponseRecorder { t.Helper() req := httptest.NewRequest(method, path, strings.NewReader(body)) if body != "" { req.Header.Set("Content-Type", "application/json") } if cookie != "" { req.AddCookie(&http.Cookie{Name: "vwr", Value: cookie}) } rec := httptest.NewRecorder() h.ServeHTTP(rec, req) return rec } // viewerCookieFor performs a request without the vwr cookie and extracts the // one the viewer middleware sets in the response. func viewerCookieFor(t *testing.T, h http.Handler, path string) string { t.Helper() rec := doReq(t, h, "GET", path, "", "") for _, c := range rec.Result().Cookies() { if c.Name == "vwr" { return c.Value } } t.Fatal("vwr cookie not set") return "" } func TestMineCreateListDelete(t *testing.T) { globalLimiter = newLimiter() // fresh rate-limit buckets st, err := store.OpenStore(":memory:") if err != nil { t.Fatal(err) } ui, err := web.New() if err != nil { t.Fatal(err) } cfg := Config{MaxTextBytes: 5 * 1024 * 1024} ss := NewTestSettingsStore(t, cfg) globalSettingsFn = ss.get t.Cleanup(func() { globalSettingsFn = nil }) a := &apiServer{store: st, cfg: cfg, ui: ui, settings: ss, adminKey: "test-admin-key"} h := a.routes() alice := viewerCookieFor(t, h, "/public") if alice == "" { t.Fatal("no viewer cookie issued") } // create with alice's cookie -> stored viewer id rec := doReq(t, h, "POST", "/api/pastes", alice, `{"content":"hello mine"}`) if rec.Code != 201 { t.Fatalf("create: %d %s", rec.Code, rec.Body.String()) } var created struct{ ID string } json.Unmarshal(rec.Body.Bytes(), &created) if created.ID == "" { t.Fatal("no id returned") } // owner sees it in /api/mine rec = doReq(t, h, "GET", "/api/mine", alice, "") if rec.Code != 200 { t.Fatalf("mine: %d", rec.Code) } var list struct { Total int `json:"total"` Items []struct{ ID string `json:"id"` } `json:"items"` } json.Unmarshal(rec.Body.Bytes(), &list) if list.Total != 1 || len(list.Items) != 1 || list.Items[0].ID != created.ID { t.Fatalf("mine list: total=%d items=%v", list.Total, list.Items) } // a different browser's cookie does NOT see it bob := viewerCookieFor(t, h, "/public") rec = doReq(t, h, "GET", "/api/mine", bob, "") json.Unmarshal(rec.Body.Bytes(), &list) if list.Total != 0 { t.Fatalf("other browser sees %d pastes, want 0", list.Total) } // delete enforcement: bob cannot delete alice's paste rec = doReq(t, h, "DELETE", "/api/pastes/"+created.ID, bob, "") if rec.Code != 403 { t.Fatalf("bob delete: %d, want 403", rec.Code) } // owner can delete rec = doReq(t, h, "DELETE", "/api/pastes/"+created.ID, alice, "") if rec.Code != 200 { t.Fatalf("alice delete: %d", rec.Code) } rec = doReq(t, h, "GET", "/api/mine", alice, "") json.Unmarshal(rec.Body.Bytes(), &list) if list.Total != 0 { t.Fatalf("after delete, mine total=%d, want 0", list.Total) } }