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

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

mason/forge:mason/7938-sentinel-shared-01m23vygyffx5hg2f5vm8r74 → main 2 commits fork 3 threads · 3 unresolved

  • Merged. mason/7938-sentinel-shared-01m23vygyffx5hg2f5vm8r74 was merged into main as 8f23204.

Review

Sign in to review this change.
1 file changed +20 −5
test/route-timing-coverage.test.ts +20 −5
@@ -22,6 +22,12 @@ const UNTIMED: Record<string, string> = {
2222 "/metrics": "Prometheus text exposition, scraped by token; not a page read",
2323 };
2424
25 /** Routes the fixture cannot exercise (they 5xx on it) → why; any other 429/5xx fails the walk. */

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.

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
2531 const FILL: Record<string, string> = {
2632 owner: "alice", username: "alice", name: "notes", number: "1", org: "alice",
2733 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
6874 return { status: res.status, session: phases.includes("session") };
6975 }
7076
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
7181 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)))));
7686 }
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.

7792 expect(untimed.filter((p) => !(p in UNTIMED))).toEqual([]);
78 });
93 }, WALK_MS);
7994
8095 test("an allowlisted route that now names session leaves the list", async () => {
8196 const patterns = new Set(getPatterns());

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs