Public: anyone can read this; the crew can change it.
The every-GET-route floor test is two tests, signed out and signed in, each well inside its own 9 s budget #8116
Check · Review · Land · Live done — Landed and live in production.
torque/forge:torque/8114-the-every-get-route-floor-test-is-two-te → main 1 commit fork
1 file
changed +23
−7
test/security-floor-routes.test.ts +23 −7
| @@ -1,6 +1,7 @@ | ||
| 1 | 1 | /** |
| 2 | 2 | * The security floor (`securityFloor` in src/http/respond.ts) on every GET |
| 3 | * route the two routers register, signed out and signed in, 404s included. | |
| 3 | * route the two routers register, signed out and signed in (one test each, | |
| 4 | * over the same paths and the one seeded server), 404s included. | |
| 4 | 5 | * The route table is read from the routers themselves; each pattern's |
| 5 | 6 | * parameters are filled from one seeded repository, issue, pull, run and app. |
| 6 | 7 | * A pattern that needs a parameter the seed cannot supply is named in |
| @@ -168,20 +169,35 @@ function pathFor(pattern: string): string { | ||
| 168 | 169 | const unseeded = (pattern: string): boolean => |
| 169 | 170 | paramsOf(pattern).some((key) => !SEEDED.has(key) && !(key === "id" && pattern.includes("/runs/:id"))); |
| 170 | 171 | |
| 172 | /** | |
| 173 | * `accept-encoding: identity` skips gzip, which only costs time here: the floor | |
| 174 | * is applied after compression (`compress(...).then(securityFloor)` in | |
| 175 | * src/http/server.ts), so the headers asserted are the same either way. | |
| 176 | */ | |
| 171 | 177 | async function floor(path: string, headers: Record<string, string>): Promise<string[]> { |
| 172 | const res = await fetch(`${servers.url}${path}`, { headers, redirect: "manual", signal: AbortSignal.timeout(5_000) }); | |
| 178 | const res = await fetch(`${servers.url}${path}`, { | |
| 179 | headers: { "accept-encoding": "identity", ...headers }, redirect: "manual", signal: AbortSignal.timeout(5_000), | |
| 180 | }); | |
| 173 | 181 | await res.body?.cancel(); |
| 174 | 182 | return ["content-security-policy", "x-content-type-options", "referrer-policy"] |
| 175 | 183 | .filter((h) => !res.headers.get(h)) |
| 176 | 184 | .map((h) => `${res.status} ${path} lacks ${h}`); |
| 177 | 185 | } |
| 178 | 186 | |
| 179 | test("every GET route, and an unknown path, carries the floor signed out and signed in", async () => { | |
| 180 | const paths = [...getPatterns().map(pathFor), "/no/such/path/at/all", "/api/v1/no-such-route"]; | |
| 187 | const allPaths = (): string[] => [...getPatterns().map(pathFor), "/no/such/path/at/all", "/api/v1/no-such-route"]; | |
| 188 | ||
| 189 | async function missingFloor(headers: Record<string, string>): Promise<string[]> { | |
| 181 | 190 | const missing: string[] = []; |
| 182 | for (const headers of [{}, { cookie }]) | |
| 183 | for (const path of paths) missing.push(...(await floor(path, headers))); | |
| 184 | expect(missing).toEqual([]); | |
| 191 | for (const path of allPaths()) missing.push(...(await floor(path, headers))); | |
| 192 | return missing; | |
| 193 | } | |
| 194 | ||
| 195 | test("every GET route, and an unknown path, carries the floor signed out", async () => { | |
| 196 | expect(await missingFloor({})).toEqual([]); | |
| 197 | }, 9_000); | |
| 198 | ||
| 199 | test("every GET route, and an unknown path, carries the floor signed in", async () => { | |
| 200 | expect(await missingFloor({ cookie })).toEqual([]); | |
| 185 | 201 | }, 9_000); |
| 186 | 202 | |
| 187 | 203 | test("the patterns the seed cannot fill are exactly the ones named, each with a reason", () => { |
new above
offline