package conformance_test import ( "encoding/json" "net/http" "strconv" "strings" "testing" "amadan.net/oficina/suite" "amadan.net/oficina/suite/conformance" ) // referenceServer is a correct server half, built straight from the fixture. // It exists so the harness can be tested against something that should pass — // and, with its behaviour bent, against things that should fail. type referenceServer struct { fixture suite.CustodyFixture // The bugs each broken variant introduces. forbidInsteadOfNotFound bool // answers 403 where the contract says 404 honourGrantOverCustody bool // answers VISIBLE for a granted item leakVisibilityInFeed bool // adds a field the feed row may not carry leakBodyInRefusal bool // right status, and the item's secrets in the body wrongBodyForTeamItem bool // 200, carrying a different item's bytes tellExistenceApart bool // a different refusal for "never existed" ignoreAskerAndRevision bool // VISIBLE for any asker, at any revision } func (s *referenceServer) find(itemID string) (suite.CustodyCase, bool) { for _, c := range s.fixture.Cases { if c.ItemID == itemID { return c, true } } return suite.CustodyCase{}, false } func (s *referenceServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { switch { case strings.HasPrefix(r.URL.Path, suite.RouteS3+"/"): s.serveS3(w, r) case r.URL.Path == suite.RouteS2: s.serveS2(w, r) case r.URL.Path == suite.RouteS1: s.serveS1(w, r) default: http.NotFound(w, r) } } func (s *referenceServer) serveS3(w http.ResponseWriter, r *http.Request) { id := strings.TrimPrefix(r.URL.Path, suite.RouteS3+"/") c, ok := s.find(id) if !ok { w.WriteHeader(404) if s.tellExistenceApart { // A different refusal for something that never existed lets a // consumer separate "no such item" from "not yours", which is // exactly what §4.2 forbids. w.Write([]byte("no such item")) } return } if c.Rung == suite.RungTeam { w.WriteHeader(200) if s.wrongBodyForTeamItem { w.Write([]byte("the bytes of some other document entirely")) return } w.Write([]byte(c.S3Body)) return } if s.forbidInsteadOfNotFound { w.WriteHeader(403) return } w.WriteHeader(404) if s.leakBodyInRefusal { // The status the contract demands, and the secret in the body anyway. w.Write([]byte("refused: " + c.Name + " at revision " + strconv.FormatInt(c.Revision, 10))) } } func (s *referenceServer) serveS2(w http.ResponseWriter, r *http.Request) { var req suite.S2Request if err := json.NewDecoder(r.Body).Decode(&req); err != nil { w.WriteHeader(400) return } type result struct { ItemID string `json:"item_id"` Revision int64 `json:"revision"` Result suite.Authorization `json:"result"` } out := struct { Results []result `json:"results"` }{} stranger := req.Asker.Value != s.fixture.Asker.Value for _, p := range req.Pairs { c, ok := s.find(p.ItemID) answer := suite.NOT_VISIBLE switch { case !ok: answer = suite.NOT_VISIBLE case s.ignoreAskerAndRevision: // Answers from the rung alone: any asker, any revision. This is // the shape a server takes when nobody ever varied either. if c.Rung == suite.RungTeam { answer = suite.VISIBLE } case stranger: // No team standing reaches nothing, the team rung included. answer = suite.NOT_VISIBLE case c.Rung == suite.RungTeam && p.Revision != c.Revision: answer = suite.STALE case c.Rung == suite.RungTeam: answer = suite.VISIBLE case s.honourGrantOverCustody && c.Rung == suite.RungGranted: // The bug the intersection exists to prevent: answering from the // grants join alone, so a grantee learns a non-team item exists. answer = suite.VISIBLE } out.Results = append(out.Results, result{ItemID: p.ItemID, Revision: p.Revision, Result: answer}) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(out) } func (s *referenceServer) serveS1(w http.ResponseWriter, r *http.Request) { rows := []map[string]any{} for _, c := range s.fixture.Cases { row := map[string]any{ "item_id": c.ItemID, "revision": c.Revision, "gone": c.Rung != suite.RungTeam, "touched_at": "2026-09-05T09:00:00Z", } if s.leakVisibilityInFeed { row["visible_to_team"] = c.Rung == suite.RungTeam } rows = append(rows, row) } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]any{"cursor": "c-" + strconv.Itoa(len(rows)), "rows": rows}) } func newReference(t *testing.T) *referenceServer { t.Helper() f, err := suite.LoadCustodyFixture() if err != nil { t.Fatalf("loading fixture: %v", err) } return &referenceServer{fixture: f} } // A correct server half passes. Without this the harness could fail // everything and still catch every bug below. func TestHarnessPassesACorrectServer(t *testing.T) { report := conformance.Check(newReference(t)) if len(report.Failures) != 0 { t.Errorf("a correct server failed conformance: %v", report.Failures) } } // Gate 3's real target. A server answering 403 for a non-team item tells the // caller the item exists, which is what "404, never 403" hides. func TestHarnessCatches403ForANonTeamItem(t *testing.T) { s := newReference(t) s.forbidInsteadOfNotFound = true report := conformance.Check(s) if len(report.Failures) == 0 { t.Error("harness passed a server answering 403 where 404 is required") } } // §4.3's custody intersection. A server answering from its grants join alone // says VISIBLE for an individually granted item, and a consumer holding that // item's id learns it still exists. func TestHarnessCatchesGrantBeatingCustody(t *testing.T) { s := newReference(t) s.honourGrantOverCustody = true report := conformance.Check(s) if len(report.Failures) == 0 { t.Error("harness passed a server whose S2 ignored the custody intersection") } } // Status alone is not enough. A server can return the required 404 and put // the item's secrets in the body, and every caller that reads the raw response // gets them. GPT-6 module review, finding 3. func TestHarnessCatchesASecretInARefusalBody(t *testing.T) { s := newReference(t) s.leakBodyInRefusal = true report := conformance.Check(s) if len(report.Failures) == 0 { t.Error("harness passed a server leaking the item in its 404 body") } } // A 200 carrying the wrong item's bytes is a leak in the other direction, and // checking only the status misses it. func TestHarnessCatchesTheWrongBodyForATeamItem(t *testing.T) { s := newReference(t) s.wrongBodyForTeamItem = true report := conformance.Check(s) if len(report.Failures) == 0 { t.Error("harness passed a server returning another item's bytes") } } // "Not team-visible" and "does not exist" must be the same answer, and no // fixture case can prove it — every case is an item that exists. func TestHarnessCatchesDistinguishableNonexistence(t *testing.T) { s := newReference(t) s.tellExistenceApart = true report := conformance.Check(s) if len(report.Failures) == 0 { t.Error("harness passed a server whose refusal reveals whether the item exists") } } // S2 carries a named asker and a revision, and a server can pass a // status-and-rung harness while reading neither. GPT-6 module review, // finding 5. func TestHarnessCatchesAServerIgnoringAskerAndRevision(t *testing.T) { s := newReference(t) s.ignoreAskerAndRevision = true report := conformance.Check(s) if len(report.Failures) == 0 { t.Error("harness passed a server that answers from the rung alone, reading neither asker nor revision") } } // The leak memoria's draft 1 shipped: a visibility flag on a feed row. func TestHarnessCatchesAFeedRowCarryingVisibility(t *testing.T) { s := newReference(t) s.leakVisibilityInFeed = true report := conformance.Check(s) if len(report.Failures) == 0 { t.Error("harness passed a feed row carrying a visibility flag") } }