oficina / suite Public

Clone
git clone https://amadan.net/oficina/suite

Plain git — no account needed to clone.

Download

Download this file

1package conformance_test
2
3import (
4 "encoding/json"
5 "net/http"
6 "strconv"
7 "strings"
8 "testing"
9
10 "amadan.net/oficina/suite"
11 "amadan.net/oficina/suite/conformance"
12)
13
14// referenceServer is a correct server half, built straight from the fixture.
15// It exists so the harness can be tested against something that should pass —
16// and, with its behaviour bent, against things that should fail.
17type referenceServer struct {
18 fixture suite.CustodyFixture
19
20 // The bugs each broken variant introduces.
21 forbidInsteadOfNotFound bool // answers 403 where the contract says 404
22 honourGrantOverCustody bool // answers VISIBLE for a granted item
23 leakVisibilityInFeed bool // adds a field the feed row may not carry
24 leakBodyInRefusal bool // right status, and the item's secrets in the body
25 wrongBodyForTeamItem bool // 200, carrying a different item's bytes
26 tellExistenceApart bool // a different refusal for "never existed"
27 ignoreAskerAndRevision bool // VISIBLE for any asker, at any revision
28}
29
30func (s *referenceServer) find(itemID string) (suite.CustodyCase, bool) {
31 for _, c := range s.fixture.Cases {
32 if c.ItemID == itemID {
33 return c, true
34 }
35 }
36 return suite.CustodyCase{}, false
37}
38
39func (s *referenceServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
40 switch {
41 case strings.HasPrefix(r.URL.Path, suite.RouteS3+"/"):
42 s.serveS3(w, r)
43 case r.URL.Path == suite.RouteS2:
44 s.serveS2(w, r)
45 case r.URL.Path == suite.RouteS1:
46 s.serveS1(w, r)
47 default:
48 http.NotFound(w, r)
49 }
50}
51
52func (s *referenceServer) serveS3(w http.ResponseWriter, r *http.Request) {
53 id := strings.TrimPrefix(r.URL.Path, suite.RouteS3+"/")
54 c, ok := s.find(id)
55 if !ok {
56 w.WriteHeader(404)
57 if s.tellExistenceApart {
58 // A different refusal for something that never existed lets a
59 // consumer separate "no such item" from "not yours", which is
60 // exactly what §4.2 forbids.
61 w.Write([]byte("no such item"))
62 }
63 return
64 }
65 if c.Rung == suite.RungTeam {
66 w.WriteHeader(200)
67 if s.wrongBodyForTeamItem {
68 w.Write([]byte("the bytes of some other document entirely"))
69 return
70 }
71 w.Write([]byte(c.S3Body))
72 return
73 }
74 if s.forbidInsteadOfNotFound {
75 w.WriteHeader(403)
76 return
77 }
78 w.WriteHeader(404)
79 if s.leakBodyInRefusal {
80 // The status the contract demands, and the secret in the body anyway.
81 w.Write([]byte("refused: " + c.Name + " at revision " + strconv.FormatInt(c.Revision, 10)))
82 }
83}
84
85func (s *referenceServer) serveS2(w http.ResponseWriter, r *http.Request) {
86 var req suite.S2Request
87 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
88 w.WriteHeader(400)
89 return
90 }
91
92 type result struct {
93 ItemID string `json:"item_id"`
94 Revision int64 `json:"revision"`
95 Result suite.Authorization `json:"result"`
96 }
97 out := struct {
98 Results []result `json:"results"`
99 }{}
100
101 stranger := req.Asker.Value != s.fixture.Asker.Value
102
103 for _, p := range req.Pairs {
104 c, ok := s.find(p.ItemID)
105 answer := suite.NOT_VISIBLE
106 switch {
107 case !ok:
108 answer = suite.NOT_VISIBLE
109
110 case s.ignoreAskerAndRevision:
111 // Answers from the rung alone: any asker, any revision. This is
112 // the shape a server takes when nobody ever varied either.
113 if c.Rung == suite.RungTeam {
114 answer = suite.VISIBLE
115 }
116
117 case stranger:
118 // No team standing reaches nothing, the team rung included.
119 answer = suite.NOT_VISIBLE
120
121 case c.Rung == suite.RungTeam && p.Revision != c.Revision:
122 answer = suite.STALE
123
124 case c.Rung == suite.RungTeam:
125 answer = suite.VISIBLE
126
127 case s.honourGrantOverCustody && c.Rung == suite.RungGranted:
128 // The bug the intersection exists to prevent: answering from the
129 // grants join alone, so a grantee learns a non-team item exists.
130 answer = suite.VISIBLE
131 }
132 out.Results = append(out.Results, result{ItemID: p.ItemID, Revision: p.Revision, Result: answer})
133 }
134
135 w.Header().Set("Content-Type", "application/json")
136 json.NewEncoder(w).Encode(out)
137}
138
139func (s *referenceServer) serveS1(w http.ResponseWriter, r *http.Request) {
140 rows := []map[string]any{}
141 for _, c := range s.fixture.Cases {
142 row := map[string]any{
143 "item_id": c.ItemID,
144 "revision": c.Revision,
145 "gone": c.Rung != suite.RungTeam,
146 "touched_at": "2026-09-05T09:00:00Z",
147 }
148 if s.leakVisibilityInFeed {
149 row["visible_to_team"] = c.Rung == suite.RungTeam
150 }
151 rows = append(rows, row)
152 }
153 w.Header().Set("Content-Type", "application/json")
154 json.NewEncoder(w).Encode(map[string]any{"cursor": "c-" + strconv.Itoa(len(rows)), "rows": rows})
155}
156
157func newReference(t *testing.T) *referenceServer {
158 t.Helper()
159 f, err := suite.LoadCustodyFixture()
160 if err != nil {
161 t.Fatalf("loading fixture: %v", err)
162 }
163 return &referenceServer{fixture: f}
164}
165
166// A correct server half passes. Without this the harness could fail
167// everything and still catch every bug below.
168func TestHarnessPassesACorrectServer(t *testing.T) {
169 report := conformance.Check(newReference(t))
170
171 if len(report.Failures) != 0 {
172 t.Errorf("a correct server failed conformance: %v", report.Failures)
173 }
174}
175
176// Gate 3's real target. A server answering 403 for a non-team item tells the
177// caller the item exists, which is what "404, never 403" hides.
178func TestHarnessCatches403ForANonTeamItem(t *testing.T) {
179 s := newReference(t)
180 s.forbidInsteadOfNotFound = true
181
182 report := conformance.Check(s)
183
184 if len(report.Failures) == 0 {
185 t.Error("harness passed a server answering 403 where 404 is required")
186 }
187}
188
189// §4.3's custody intersection. A server answering from its grants join alone
190// says VISIBLE for an individually granted item, and a consumer holding that
191// item's id learns it still exists.
192func TestHarnessCatchesGrantBeatingCustody(t *testing.T) {
193 s := newReference(t)
194 s.honourGrantOverCustody = true
195
196 report := conformance.Check(s)
197
198 if len(report.Failures) == 0 {
199 t.Error("harness passed a server whose S2 ignored the custody intersection")
200 }
201}
202
203// Status alone is not enough. A server can return the required 404 and put
204// the item's secrets in the body, and every caller that reads the raw response
205// gets them. GPT-6 module review, finding 3.
206func TestHarnessCatchesASecretInARefusalBody(t *testing.T) {
207 s := newReference(t)
208 s.leakBodyInRefusal = true
209
210 report := conformance.Check(s)
211
212 if len(report.Failures) == 0 {
213 t.Error("harness passed a server leaking the item in its 404 body")
214 }
215}
216
217// A 200 carrying the wrong item's bytes is a leak in the other direction, and
218// checking only the status misses it.
219func TestHarnessCatchesTheWrongBodyForATeamItem(t *testing.T) {
220 s := newReference(t)
221 s.wrongBodyForTeamItem = true
222
223 report := conformance.Check(s)
224
225 if len(report.Failures) == 0 {
226 t.Error("harness passed a server returning another item's bytes")
227 }
228}
229
230// "Not team-visible" and "does not exist" must be the same answer, and no
231// fixture case can prove it — every case is an item that exists.
232func TestHarnessCatchesDistinguishableNonexistence(t *testing.T) {
233 s := newReference(t)
234 s.tellExistenceApart = true
235
236 report := conformance.Check(s)
237
238 if len(report.Failures) == 0 {
239 t.Error("harness passed a server whose refusal reveals whether the item exists")
240 }
241}
242
243// S2 carries a named asker and a revision, and a server can pass a
244// status-and-rung harness while reading neither. GPT-6 module review,
245// finding 5.
246func TestHarnessCatchesAServerIgnoringAskerAndRevision(t *testing.T) {
247 s := newReference(t)
248 s.ignoreAskerAndRevision = true
249
250 report := conformance.Check(s)
251
252 if len(report.Failures) == 0 {
253 t.Error("harness passed a server that answers from the rung alone, reading neither asker nor revision")
254 }
255}
256
257// The leak memoria's draft 1 shipped: a visibility flag on a feed row.
258func TestHarnessCatchesAFeedRowCarryingVisibility(t *testing.T) {
259 s := newReference(t)
260 s.leakVisibilityInFeed = true
261
262 report := conformance.Check(s)
263
264 if len(report.Failures) == 0 {
265 t.Error("harness passed a feed row carrying a visibility flag")
266 }
267}
268