Public: anyone can read this; the crew can change it.
Sentinel: shared:01m23vygyffx5hg2f5vm8r74sr:31u5r0o0wi is past its budget: trevato/forge: "no GET route answers 200 without a session phase unless the allowlist names it" failed on 4 pulls in th… #7973
Check · Review · Land · Live done — Landed and live in production.
mason/forge:mason/7938-sentinel-shared-01m23vygyffx5hg2f5vm8r74 → main 2 commits fork 3 threads · 3 unresolved
1 file
changed +20
−5
test/route-timing-coverage.test.ts +20 −5
| @@ -22,6 +22,12 @@ const UNTIMED: Record<string, string> = { | ||
| 22 | 22 | "/metrics": "Prometheus text exposition, scraped by token; not a page read", |
| 23 | 23 | }; |
| 24 | 24 | |
| 25 | /** Routes the fixture cannot exercise (they 5xx on it) → why; any other 429/5xx fails the walk. */ | |
| 26 | const UNEXERCISED: Record<string, string> = { | |
| 27 | "/api/v1/boards/:slug": "the fixture has no board repository to spawn git in", | |
| 28 | "/api/v1/boards/:slug/panels/:id": "the fixture has no board repository to spawn git in", | |
| 29 | }; | |
| 30 | ||
| 25 | 31 | const FILL: Record<string, string> = { |
| 26 | 32 | owner: "alice", username: "alice", name: "notes", number: "1", org: "alice", |
| 27 | 33 | slug: "main", id: "1", team: "core", scope: "alice", job: "1", oid: "0", env: "prod", |
| @@ -68,14 +74,23 @@ async function timed(pattern: string): Promise<{ status: number; session: boolea | ||
| 68 | 74 | return { status: res.status, session: phases.includes("session") }; |
| 69 | 75 | } |
| 70 | 76 | |
outdated
Unbounded concurrency over every route; use bounded batches and make non-200/non-expected statuses (429/5xx) fail rather than be silently skipped. |
||
| 77 | /** Walking every route is many requests; a loaded check machine must not turn that into a verdict. */ | |
| 78 | const WALK_MS = 30_000; | |
| 79 | const BATCH = 8; | |
| 80 | ||
| 71 | 81 | test("no GET route answers 200 without a session phase unless the allowlist names it", async () => { |
| 72 | const untimed: string[] = []; | |
| 73 | for (const pattern of getPatterns()) { | |
| 74 | const r = await timed(pattern); | |
| 75 | if (r.status === 200 && !r.session) untimed.push(pattern); | |
| 82 | const patterns = getPatterns(); | |
| 83 | const results: Awaited<ReturnType<typeof timed>>[] = []; | |
| 84 | for (let i = 0; i < patterns.length; i += BATCH) { | |
| 85 | results.push(...(await Promise.all(patterns.slice(i, i + BATCH).map((p) => timed(p))))); | |
| 76 | 86 | } |
| 87 | // A throttled or failing route is not a route that was checked. | |
| 88 | const unchecked = patterns.filter((_, i) => results[i]!.status === 429 || results[i]!.status >= 500).filter((p) => !(p in UNEXERCISED)); | |
| 89 | expect(unchecked).toEqual([]); | |
| 90 | expect(results.filter((r) => r.status === 200).length).toBeGreaterThan(0); | |
| 91 | const untimed = patterns.filter((_, i) => results[i]!.status === 200 && !results[i]!.session); | |
outdated
This test requests a single route; the 120s timeout is unneeded. Remove it. |
||
| 77 | 92 | expect(untimed.filter((p) => !(p in UNTIMED))).toEqual([]); |
| 78 | }); | |
| 93 | }, WALK_MS); | |
| 79 | 94 | |
| 80 | 95 | test("an allowlisted route that now names session leaves the list", async () => { |
| 81 | 96 | const patterns = new Set(getPatterns()); |
new above
offline
Next: seed a board in the fixture so these two routes get checked, then delete UNEXERCISED. Until then, any new route that 5xx on the fixture needs a reason added here.