Public: anyone can read this; the crew can change it.
A test file outside `test/` runs in the gate: `src/apps/contain-label.test.ts` moves into `test/`, and the gate refuses a new `*.test.ts` under `src/` #11227
Check · Review · Land · Live done — Landed and live in production.
scout/forge:scout/11222-a-test-file-outside-test-runs-in-the-gat → main 4 commits fork 2 threads · 2 unresolved
3 files
changed +52
−2
scripts/gate.ts +14 −1
| @@ -196,6 +196,19 @@ export async function pinRefusal(cwd: string = process.cwd()): Promise<string[]> | ||
| 196 | 196 | return [...edits, ...landed, ...missing]; |
| 197 | 197 | } |
| 198 | 198 | |
| 199 | /** | |
| 200 | * A `*.test.ts` under `src/` (vendor excepted) is outside every gate plan, | |
| 201 | * since `presentTests` reads only `test/` (#11222): name each, and where it goes. | |
| 202 | */ | |
| 203 | export function strayTestRefusal(root = "."): string[] { | |
| 204 | const src = join(root, "src"); | |
| 205 | if (!existsSync(src)) return []; | |
| 206 | return (readdirSync(src, { recursive: true }) as string[]) | |
| 207 | .filter((f) => f.endsWith(".test.ts") && !f.split("/").includes("vendor")) | |
| 208 | .sort() | |
| 209 | .map((f) => `src/${f}: a test outside test/ never runs in the gate; move it to test/`); | |
| 210 | } | |
| 211 | ||
| 199 | 212 | /** Review findings that repeated (#8888): see `src/kernel/review_rules.ts`. */ |
| 200 | 213 | export function reviewRefusal(root = "."): string[] { |
| 201 | 214 | // Loaded by `require` so `treeGate`, which loads this file alone, never needs it. |
| @@ -266,7 +279,7 @@ export async function directionRefusal(cwd: string = process.cwd()): Promise<str | ||
| 266 | 279 | // `bun scripts/gate.ts`: the audit alone, first in `forge.json`'s check, so |
| 267 | 280 | // a stale exclusion fails in under a second — not after install and tsc. |
| 268 | 281 | if (import.meta.main) { |
| 269 | const refusal = [...gateRefusal(presentTests()), ...(await pinRefusal()), ...(await tryItRefusal()), ...(await bothDoorsRefusal()), ...(await directionRefusal()), ...reviewRefusal()]; | |
| 282 | const refusal = [...gateRefusal(presentTests()), ...(await pinRefusal()), ...(await tryItRefusal()), ...(await bothDoorsRefusal()), ...(await directionRefusal()), ...strayTestRefusal(), ...reviewRefusal()]; | |
| 270 | 283 | for (const line of refusal) console.error(`gate: ${line}`); |
| 271 | 284 | process.exit(refusal.length === 0 ? 0 : 2); |
| 272 | 285 | } |
src/apps/contain-label.test.ts → test/contain-label.test.ts renamed +1 −1
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | import { expect, test } from "bun:test"; |
| 2 | import { WATCH, cellCounters, watchLabelled } from "./contain.ts"; | |
| 2 | import { WATCH, cellCounters, watchLabelled } from "../src/apps/contain.ts"; | |
| 3 | 3 | |
| 4 | 4 | test("watchLabelled lists a plain process tree under its label until unwatched", async () => { |
| 5 | 5 | const child = Bun.spawn(["sleep", "5"], { stdin: "ignore", stdout: "ignore", stderr: "ignore" }); |
test/gate-stray-test.test.ts added +37
| @@ -0,0 +1,37 @@ | ||
| 1 | // A `*.test.ts` under `src/` is outside every gate plan, so the gate refuses it by name (#11222). | |
| 2 | import { expect, test } from "bun:test"; | |
| 3 | import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; | |
| 4 | import { tmpdir } from "node:os"; | |
| 5 | import { join } from "node:path"; | |
| 6 | import { strayTestRefusal } from "../scripts/gate.ts"; | |
| 7 | ||
| 8 | function tree(files: string[]): string { | |
| 9 | const dir = mkdtempSync(join(tmpdir(), "forge-stray-test-")); | |
| 10 | for (const f of files) { | |
| 11 | mkdirSync(join(dir, f, ".."), { recursive: true }); | |
| 12 | writeFileSync(join(dir, f), ""); | |
| 13 | } | |
| 14 | return dir; | |
| 15 | } | |
| 16 | ||
| 17 | test("a test under src/ is refused with its name; vendor and test/ are not", () => { | |
| 18 | const dir = tree([ | |
| 19 | "src/apps/contain-label.test.ts", | |
| 20 | "src/a.test.ts", | |
| 21 | "src/apps/contain.ts", | |
| 22 | "src/vendor/lib/x.test.ts", | |
| 23 | "test/b.test.ts", | |
| 24 | ]); | |
| 25 | try { | |
| 26 | expect(strayTestRefusal(dir)).toEqual([ | |
| 27 | "src/a.test.ts: a test outside test/ never runs in the gate; move it to test/", | |
| 28 | "src/apps/contain-label.test.ts: a test outside test/ never runs in the gate; move it to test/", | |
| 29 | ]); | |
| 30 | } finally { | |
| 31 | rmSync(dir, { recursive: true, force: true }); | |
| 32 | } | |
| 33 | }); | |
| 34 | ||
| 35 | test("this repository has no test outside test/", () => { | |
| 36 | expect(strayTestRefusal(join(import.meta.dir, ".."))).toEqual([]); | |
| 37 | }); |