package media import ( "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/mithral/vellum/internal/auth" ) // getItem drives the per-item detail handler directly. PathValue is normally // filled in by the ServeMux; the test sets it explicitly. func getItem(t *testing.T, h *Handlers, u auth.User, id string) *httptest.ResponseRecorder { t.Helper() req := httptest.NewRequest(http.MethodGet, "/api/v1/items/"+id, nil) req.SetPathValue("id", id) rec := httptest.NewRecorder() h.handleItem(rec, req, u) return rec } // TestHandleItemScoping pins the per-item access chokepoint: an item in a // library the caller cannot read must be indistinguishable from a missing one. func TestHandleItemScoping(t *testing.T) { f := setupHome(t) h := mediaHandlers(t, f.db) // userA may read libA, so a1 resolves. rec := getItem(t, h, auth.User{ID: f.userA}, itoa(f.a1)) if rec.Code != http.StatusOK { t.Fatalf("accessible item status = %d, body=%s", rec.Code, rec.Body.String()) } var body struct { Item struct { ID int64 `json:"id"` LibraryID int64 `json:"library_id"` Kind string `json:"kind"` } `json:"item"` Authors []string `json:"authors"` } if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil { t.Fatalf("decode item: %v", err) } if body.Item.ID != f.a1 || body.Item.LibraryID != f.libA || body.Item.Kind != "ebooks" { t.Errorf("item = %+v, want id %d in library %d", body.Item, f.a1, f.libA) } if len(body.Authors) != 1 || body.Authors[0] != "Ada Author" { t.Errorf("authors = %v, want [Ada Author]", body.Authors) } // b1 is in libB, which userA cannot read: 404, not 403, so existence is // not disclosed. rec = getItem(t, h, auth.User{ID: f.userA}, itoa(f.b1)) if rec.Code != http.StatusNotFound { t.Errorf("inaccessible item status = %d, want 404", rec.Code) } // A user with no grants gets the same 404. rec = getItem(t, h, auth.User{ID: f.userNone}, itoa(f.a1)) if rec.Code != http.StatusNotFound { t.Errorf("no-grant item status = %d, want 404", rec.Code) } // A non-numeric id is a 400, not a panic. rec = getItem(t, h, auth.User{ID: f.userA}, "abc") if rec.Code != http.StatusBadRequest { t.Errorf("invalid id status = %d, want 400", rec.Code) } }