From 601d5ea447aab35e9e6219d88d89ff763374fc10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 22 Oct 2026 10:19:11 +0200 Subject: [PATCH 1/3] test(guards): walk the tree once in the timezone formatter guard Each of the guard's scans started its own grep over the whole of src, the generated client included, five times per run, and the floor case timed out at five seconds under a loaded unit suite. The tree is read once now, without the generated client, and a file is kept only when a plain substring says a pattern could match; each scan's result is reused by the cases that ask about it. The file runs in 1.9 s instead of 6.5 s alone, and the floor case in 2 ms instead of 2.3 s. --- .../timezone-explicit-formatter-guard.test.ts | 136 ++++++++++-------- 1 file changed, 73 insertions(+), 63 deletions(-) diff --git a/src/__tests__/timezone-explicit-formatter-guard.test.ts b/src/__tests__/timezone-explicit-formatter-guard.test.ts index 7b4977f68..1ad38aee6 100644 --- a/src/__tests__/timezone-explicit-formatter-guard.test.ts +++ b/src/__tests__/timezone-explicit-formatter-guard.test.ts @@ -1,9 +1,10 @@ import { readFileSync } from "node:fs"; -import { execFileSync } from "node:child_process"; import { resolve } from "node:path"; import { describe, expect, it } from "vitest"; +import { walkSourceFiles } from "./helpers/source-files"; + /** * A formatter that renders an instant must say which calendar it renders in. * @@ -29,6 +30,54 @@ import { describe, expect, it } from "vitest"; const REPO_ROOT = resolve(__dirname, "../.."); +/** A source file under `src/`, by its repo-relative path, and its text. */ +interface SourceFile { + file: string; + source: string; +} + +/** Whatever any scan below looks for, as plain substrings. */ +const MARKERS = [ + "Intl.DateTimeFormat", + ".toLocaleDateString", + ".toLocaleTimeString", + "getDateTimeFormat", + "fmt.date", +]; + +let candidates: SourceFile[] | null = null; + +/** + * Every source file that mentions one of the markers, read once per run. + * + * Each scan used to start its own `grep -r` over the whole of `src/`, + * generated client included, five times per run; under a loaded unit suite + * that alone ran past the 5 s test timeout. The tree is walked once now, the + * generated Prisma client left out (it renders no dates, and no scan has ever + * matched in it), and each file is kept only when a plain substring test says + * a pattern could match, before any regular expression runs. + */ +function candidateFiles(): SourceFile[] { + candidates ??= walkSourceFiles(resolve(REPO_ROOT, "src"), { floor: 3000 }) + .filter((rel) => !rel.startsWith("generated/")) + .map((rel) => ({ + file: `src/${rel}`, + source: readFileSync(resolve(REPO_ROOT, "src", rel), "utf8"), + })) + .filter(({ source }) => MARKERS.some((marker) => source.includes(marker))); + return candidates; +} + +/** The candidate files a pattern matches, test files left in. */ +function filesMatching(pattern: RegExp, under = "src/"): SourceFile[] { + return candidateFiles().filter( + ({ file, source }) => file.startsWith(under) && pattern.test(source), + ); +} + +const ANY_FORMATTER = + /new Intl\.DateTimeFormat|\.toLocale(Date|Time)String\(|getDateTimeFormat\(/; + /** * Bare formatter sites that are correct as they stand. Keep this short: it is * an inventory of exceptions, not a parking space. Every entry says why, and @@ -97,26 +146,12 @@ function topLevelArgCount(args: string): number { } function bareFormatterSites(): Array<{ file: string; line: number }> { - const listed = execFileSync( - "grep", - [ - "-rlE", - "new Intl\\.DateTimeFormat|\\.toLocale(Date|Time)String\\(|getDateTimeFormat\\(", - "src", - "--include=*.ts", - "--include=*.tsx", - ], - { cwd: REPO_ROOT, encoding: "utf8" }, - ) - .trim() - .split("\n") - .filter(Boolean) + const listed = filesMatching(ANY_FORMATTER) // Tests pin instants on purpose and are not user-facing surfaces. - .filter((f) => !f.includes("__tests__")); + .filter(({ file }) => !file.includes("__tests__")); const hits: Array<{ file: string; line: number }> = []; - for (const file of listed) { - const source = readFileSync(resolve(REPO_ROOT, file), "utf8"); + for (const { file, source } of listed) { for (const pattern of FORMATTER_PATTERNS) { pattern.lastIndex = 0; for (let m = pattern.exec(source); m !== null; m = pattern.exec(source)) { @@ -158,21 +193,7 @@ describe("date formatters name the calendar they render in", () => { it("finds formatter sites at all (the guard must not pass vacuously)", () => { // If this drops to zero the grep has stopped matching and every assertion // below became free. - const all = execFileSync( - "grep", - [ - "-rlE", - "new Intl\\.DateTimeFormat|\\.toLocale(Date|Time)String\\(|getDateTimeFormat\\(", - "src", - "--include=*.ts", - "--include=*.tsx", - ], - { cwd: REPO_ROOT, encoding: "utf8" }, - ) - .trim() - .split("\n") - .filter(Boolean); - expect(all.length).toBeGreaterThan(5); + expect(filesMatching(ANY_FORMATTER).length).toBeGreaterThan(5); }); it("has no unexplained formatter without an explicit timeZone", () => { @@ -241,26 +262,17 @@ describe("date formatters name the calendar they render in", () => { describe("components format dates through useFormatters", () => { const CALL = /\bgetDateTimeFormat\s*\(/g; + let scanned: { offenders: string[]; calls: number } | null = null; + function scan() { - const files = execFileSync( - "grep", - [ - "-rlE", - "getDateTimeFormat\\s*\\(", - "src/components", - "--include=*.ts", - "--include=*.tsx", - ], - { cwd: REPO_ROOT, encoding: "utf8" }, - ) - .trim() - .split("\n") - .filter(Boolean) - .filter((f) => !f.includes("__tests__")); + if (scanned) return scanned; + const files = filesMatching( + /getDateTimeFormat\s*\(/, + "src/components/", + ).filter(({ file }) => !file.includes("__tests__")); const offenders: string[] = []; let calls = 0; - for (const file of files) { - const source = readFileSync(resolve(REPO_ROOT, file), "utf8"); + for (const { file, source } of files) { CALL.lastIndex = 0; for (let m = CALL.exec(source); m; m = CALL.exec(source)) { const lineStart = source.lastIndexOf("\n", m.index) + 1; @@ -275,7 +287,8 @@ describe("components format dates through useFormatters", () => { } } } - return { offenders, calls }; + scanned = { offenders, calls }; + return scanned; } it("still sees the cache's calls in components (the matcher matches)", () => { @@ -317,20 +330,16 @@ describe("a stated date is formatted on its calendar date", () => { const CALENDAR_CALL = /\bfmt\.dateShortSmartCalendar\s*\(/g; const NOON_ANCHOR = /T12:00:00/; + let scanned: { offenders: string[]; calendarCalls: number } | null = null; + function scan() { - const files = execFileSync( - "grep", - ["-rlE", "fmt\\.date", "src", "--include=*.ts", "--include=*.tsx"], - { cwd: REPO_ROOT, encoding: "utf8" }, - ) - .trim() - .split("\n") - .filter(Boolean) - .filter((f) => !f.includes("__tests__")); + if (scanned) return scanned; + const files = filesMatching(/fmt\.date/).filter( + ({ file }) => !file.includes("__tests__"), + ); const offenders: string[] = []; let calendarCalls = 0; - for (const file of files) { - const source = readFileSync(resolve(REPO_ROOT, file), "utf8"); + for (const { file, source } of files) { calendarCalls += (source.match(CALENDAR_CALL) ?? []).length; if (/const fmt = makeBucketLabelFormatters\(/.test(source)) continue; ZONED_CALL.lastIndex = 0; @@ -343,7 +352,8 @@ describe("a stated date is formatted on its calendar date", () => { } } } - return { offenders, calendarCalls }; + scanned = { offenders, calendarCalls }; + return scanned; } it("finds the calendar formatter in use (the matcher still matches)", () => { From 4dc3bd46bf09ab1d9c3da2e21e5a69e018526292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 22 Oct 2026 10:30:50 +0200 Subject: [PATCH 2/3] test(guards): read the source tree once per worker through a shared index walkSourceFiles now lists each indexed folder (src, e2e, prisma) once per worker and filters it by prefix, readSource and readSourceFolder read through the same memoised file cache, and readSourceText is the cached read for guards that read files themselves. The guards project shares a module graph per worker, so a file is read once however many guards ask for it. sourcesContaining adds the substring pre-filter for sweeps that look for a few markers. Every guard over 1.5 s now reads through it; anything outside the indexed folders is read from disk as before. --- .../acting-account-boundary-guard.test.ts | 5 +- .../ai-capability-surface-guard.test.ts | 9 +- .../bearer-scope-enforcement-guard.test.ts | 5 +- .../client-fetch-limit-guard.test.ts | 5 +- src/__tests__/client-route-zod-guard.test.ts | 7 +- .../compose-env-whitelist-guard.test.ts | 7 +- ...lation-single-implementation-guard.test.ts | 23 ++-- .../daily-reads-refetch-guard.test.ts | 5 +- src/__tests__/discovery-matrix-guard.test.ts | 5 +- .../display-transform-rounding-guard.test.ts | 5 +- .../error-level-discipline-guard.test.ts | 7 +- .../external-id-stability-guard.test.ts | 8 +- src/__tests__/helpers/source-files.ts | 113 ++++++++++++++++-- .../idempotency-contract-publication.test.ts | 5 +- .../insights-cached-text-access-guard.test.ts | 11 +- .../latest-per-type-read-guard.test.ts | 21 ++-- ...ar-fit-single-implementation-guard.test.ts | 13 +- .../memory-block-egress-guard.test.ts | 5 +- .../naive-timestamp-zone-shift-guard.test.ts | 10 +- src/__tests__/no-control-bytes-guard.test.ts | 4 +- src/__tests__/oauth-kit-guard.test.ts | 6 +- .../profile-rejection-surface-guard.test.ts | 5 +- .../recent-proof-surface-guard.test.ts | 5 +- .../record-session-fence-guard.test.ts | 5 +- .../recurrence-course-anchor-guard.test.ts | 5 +- src/__tests__/session-surface-guard.test.ts | 5 +- src/__tests__/sharing-surface-guard.test.ts | 5 +- ...spread-single-implementation-guard.test.ts | 9 +- src/__tests__/sql-now-utc-guard.test.ts | 7 +- src/__tests__/step-up-elevation-guard.test.ts | 5 +- ...sorge-due-bucket-single-home-guard.test.ts | 5 +- .../workout-sport-provenance-guard.test.ts | 5 +- 32 files changed, 211 insertions(+), 129 deletions(-) diff --git a/src/__tests__/acting-account-boundary-guard.test.ts b/src/__tests__/acting-account-boundary-guard.test.ts index 107794519..5a3dc2bef 100644 --- a/src/__tests__/acting-account-boundary-guard.test.ts +++ b/src/__tests__/acting-account-boundary-guard.test.ts @@ -14,10 +14,9 @@ * those. */ import { describe, it, expect } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -30,7 +29,7 @@ function sourceFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } function filesMatching(re: RegExp): string[] { diff --git a/src/__tests__/ai-capability-surface-guard.test.ts b/src/__tests__/ai-capability-surface-guard.test.ts index e796fc236..b82232e09 100644 --- a/src/__tests__/ai-capability-surface-guard.test.ts +++ b/src/__tests__/ai-capability-surface-guard.test.ts @@ -26,14 +26,17 @@ * another module would slip it; `AI_ROUTE_PATTERNS` lists the literal shapes * the tree uses today. */ -import { readFileSync } from "node:fs"; import path from "node:path"; import { describe, expect, it } from "vitest"; import type { AiCapabilityKey } from "@/lib/ai/capabilities/types"; -import { stripComments, walkSourceFiles } from "./helpers/source-files"; +import { + readSourceText, + stripComments, + walkSourceFiles, +} from "./helpers/source-files"; const SRC = path.resolve(__dirname, ".."); @@ -64,7 +67,7 @@ const FEATURE_FLAG_SWEEP = NON_TEST_FILES.filter( ); const read = (rel: string) => - stripComments(readFileSync(path.join(SRC, rel), "utf8")); + stripComments(readSourceText(path.join(SRC, rel))); /** Request literals of routes that call a model or serve model text. */ const AI_ROUTE_PATTERNS: readonly RegExp[] = [ diff --git a/src/__tests__/bearer-scope-enforcement-guard.test.ts b/src/__tests__/bearer-scope-enforcement-guard.test.ts index 6c0298f4b..21ab2d10a 100644 --- a/src/__tests__/bearer-scope-enforcement-guard.test.ts +++ b/src/__tests__/bearer-scope-enforcement-guard.test.ts @@ -13,10 +13,9 @@ * for that review. */ import { describe, it, expect } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -33,7 +32,7 @@ function sourceFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } function filesMatching(re: RegExp): string[] { diff --git a/src/__tests__/client-fetch-limit-guard.test.ts b/src/__tests__/client-fetch-limit-guard.test.ts index c9d73875a..87b9a9f6b 100644 --- a/src/__tests__/client-fetch-limit-guard.test.ts +++ b/src/__tests__/client-fetch-limit-guard.test.ts @@ -28,10 +28,9 @@ * outside `app/api/`); a server-side `take` is not a fetch. */ import { describe, expect, it } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -178,7 +177,7 @@ function findLimitHits(file: string, source: string): Hit[] { function allHits(): Hit[] { return clientFiles().flatMap((file) => - findLimitHits(file, readFileSync(join(SRC, file), "utf8")), + findLimitHits(file, readSourceText(join(SRC, file))), ); } diff --git a/src/__tests__/client-route-zod-guard.test.ts b/src/__tests__/client-route-zod-guard.test.ts index ad2f39e9a..b7d0e207b 100644 --- a/src/__tests__/client-route-zod-guard.test.ts +++ b/src/__tests__/client-route-zod-guard.test.ts @@ -20,9 +20,10 @@ * loaded chunk may still carry zod. */ import { describe, expect, it } from "vitest"; -import { existsSync, readFileSync } from "node:fs"; +import { existsSync } from "node:fs"; import { dirname, join, normalize } from "node:path"; import ts from "typescript"; +import { readSourceText } from "./helpers/source-files"; const ROOT = join(__dirname, "..", ".."); @@ -90,7 +91,7 @@ function clientZodImports(entry: string): { file: string; path: string[] }[] { const [file, inClient] = stack.pop()!; const prior = seen.get(file); if (prior === true || (prior === false && !inClient)) continue; - const source = readFileSync(file, "utf8"); + const source = readSourceText(file); const client = inClient || isClient(source); seen.set(file, client); walked += 1; @@ -141,7 +142,7 @@ describe("client routes without the schema library", () => { // The schema module is zod by definition; walked as a client entry it // must be reported, or the walk above proves nothing. const probe = join(ROOT, "src/lib/validations/inbound-documents.ts"); - const source = readFileSync(probe, "utf8"); + const source = readSourceText(probe); expect(staticImports(source, probe).some((s) => s.startsWith("zod"))).toBe( true, ); diff --git a/src/__tests__/compose-env-whitelist-guard.test.ts b/src/__tests__/compose-env-whitelist-guard.test.ts index 027cb5262..89f9a534e 100644 --- a/src/__tests__/compose-env-whitelist-guard.test.ts +++ b/src/__tests__/compose-env-whitelist-guard.test.ts @@ -35,10 +35,9 @@ * allowlist. Both the walk and every matcher therefore assert a floor. */ import { describe, it, expect } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const ROOT = process.cwd(); const SRC = join(ROOT, "src"); @@ -151,7 +150,7 @@ function sourceFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } /** Every environment variable shipped server code reads, however it reads it. */ @@ -175,7 +174,7 @@ function runtimeReads(): Set { * repeating it. */ function composeWhitelist(): Set { - const compose = readFileSync(join(ROOT, "docker-compose.yml"), "utf8"); + const compose = readSourceText(join(ROOT, "docker-compose.yml")); const keys = new Set(); for (const line of compose.split("\n")) { const m = line.match(/^\s{2,}([A-Z][A-Z0-9_]*):\s/); diff --git a/src/__tests__/correlation-single-implementation-guard.test.ts b/src/__tests__/correlation-single-implementation-guard.test.ts index 708118b23..c796e97d6 100644 --- a/src/__tests__/correlation-single-implementation-guard.test.ts +++ b/src/__tests__/correlation-single-implementation-guard.test.ts @@ -17,11 +17,14 @@ * inline under another name slips it. Comments are stripped first; the * planted cases prove it fires. */ -import { readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; -import { stripComments, walkSourceFiles } from "./helpers/source-files"; +import { + readSourceText, + stripComments, + walkSourceFiles, +} from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -56,14 +59,8 @@ describe("one Pearson correlation", () => { }); it("both homes still hold their function", () => { - const engine = readFileSync( - join(SRC, "lib/insights/correlations.ts"), - "utf8", - ); - const wrapper = readFileSync( - join(SRC, "lib/analytics/correlations.ts"), - "utf8", - ); + const engine = readSourceText(join(SRC, "lib/insights/correlations.ts")); + const wrapper = readSourceText(join(SRC, "lib/analytics/correlations.ts")); expect(engine).toMatch(/export function pearson\s*\(/); expect(wrapper).toMatch( /export function significantPearsonCorrelation\s*\(/, @@ -73,14 +70,12 @@ describe("one Pearson correlation", () => { it("no other file defines a Pearson coefficient or calls the deleted one", () => { // The homes match the definition matcher, so a matcher gone blind fails here. const definers = sourceFiles().filter((file) => - PEARSON_DEFINITION.test( - stripComments(readFileSync(join(SRC, file), "utf8")), - ), + PEARSON_DEFINITION.test(stripComments(readSourceText(join(SRC, file)))), ); expect(definers.length).toBeGreaterThanOrEqual(2); const offenders = sourceFiles().filter((file) => { - const code = stripComments(readFileSync(join(SRC, file), "utf8")); + const code = stripComments(readSourceText(join(SRC, file))); if (DELETED.test(code)) return true; return !HOMES.has(file) && PEARSON_DEFINITION.test(code); }); diff --git a/src/__tests__/daily-reads-refetch-guard.test.ts b/src/__tests__/daily-reads-refetch-guard.test.ts index 201759d15..97bfa839b 100644 --- a/src/__tests__/daily-reads-refetch-guard.test.ts +++ b/src/__tests__/daily-reads-refetch-guard.test.ts @@ -32,10 +32,9 @@ * assertion. Both fixes were then restored and the guard re-ran green. */ import { describe, it, expect } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -91,7 +90,7 @@ function sourceFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } function count(s: string, re: RegExp): number { diff --git a/src/__tests__/discovery-matrix-guard.test.ts b/src/__tests__/discovery-matrix-guard.test.ts index 36e7d48d7..c45ebd42a 100644 --- a/src/__tests__/discovery-matrix-guard.test.ts +++ b/src/__tests__/discovery-matrix-guard.test.ts @@ -43,10 +43,9 @@ * how `bearer-scope-enforcement-guard` stayed green over a file it never saw. */ import { describe, it, expect } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -69,7 +68,7 @@ function sourceFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } function filesMatching(re: RegExp): string[] { diff --git a/src/__tests__/display-transform-rounding-guard.test.ts b/src/__tests__/display-transform-rounding-guard.test.ts index 73b928296..470ed94b8 100644 --- a/src/__tests__/display-transform-rounding-guard.test.ts +++ b/src/__tests__/display-transform-rounding-guard.test.ts @@ -23,10 +23,9 @@ * are pinned to a named allowlist, so a new converted-value surface * cannot quietly join them. */ -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; import { describe, it, expect } from "vitest"; @@ -83,7 +82,7 @@ function sourceFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } describe("display-transform rounding guard — the registry property", () => { diff --git a/src/__tests__/error-level-discipline-guard.test.ts b/src/__tests__/error-level-discipline-guard.test.ts index 027fe7e17..98459a233 100644 --- a/src/__tests__/error-level-discipline-guard.test.ts +++ b/src/__tests__/error-level-discipline-guard.test.ts @@ -40,12 +40,11 @@ * and asserts the sweep goes red without it, so every entry is shown to be * load-bearing, and the sweep is shown to find what it claims to find. */ -import { readFileSync } from "node:fs"; import { join } from "node:path"; import ts from "typescript"; import { describe, expect, it } from "vitest"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); const SCOPE = "lib/jobs/"; @@ -257,9 +256,7 @@ const ALLOWED: Readonly> = { function sweep(): Finding[] { return walkSourceFiles(join(SRC, SCOPE), { floor: 100 }) .filter((p) => !p.includes("__tests__") && !p.endsWith(".test.ts")) - .flatMap((p) => - scan(`${SCOPE}${p}`, readFileSync(join(SRC, SCOPE, p), "utf8")), - ); + .flatMap((p) => scan(`${SCOPE}${p}`, readSourceText(join(SRC, SCOPE, p)))); } function unallowed( diff --git a/src/__tests__/external-id-stability-guard.test.ts b/src/__tests__/external-id-stability-guard.test.ts index 0cffe0109..09b03fc17 100644 --- a/src/__tests__/external-id-stability-guard.test.ts +++ b/src/__tests__/external-id-stability-guard.test.ts @@ -22,10 +22,9 @@ * addition defeats them, and no test substitutes for that review. */ import { describe, it, expect } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); const VALIDATOR_MODULE = "@/lib/validations/external-id"; @@ -39,7 +38,7 @@ function sourceFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } /** @@ -377,9 +376,8 @@ describe("the intake idempotency key is never read across users", () => { }); it("the schema keeps the key unique per user, not globally", () => { - const schema = readFileSync( + const schema = readSourceText( join(process.cwd(), "prisma", "schema.prisma"), - "utf8", ); expect(schema).toContain("@@unique([userId, idempotencyKey])"); expect(schema).not.toMatch( diff --git a/src/__tests__/helpers/source-files.ts b/src/__tests__/helpers/source-files.ts index 70e92385a..3f7867917 100644 --- a/src/__tests__/helpers/source-files.ts +++ b/src/__tests__/helpers/source-files.ts @@ -51,17 +51,114 @@ * match. Every caller roots at `src/`, `e2e/` or a subtree of one. */ import { existsSync, readdirSync, readFileSync } from "node:fs"; -import { join, sep } from "node:path"; +import { join, relative, resolve, sep } from "node:path"; + +// ── The shared source index ──────────────────────────────────────────────── +// +// The guards walk and read the same tree, and the guards project shares one +// module graph per worker (`isolate: false`), so every walk and every read +// below is memoised for the life of the worker: the tree is listed once per +// top-level folder and each file is read once, however many guards ask. A +// guard that walked `src/` and read every file again for each pattern it +// tried ran for fifteen seconds; the same questions over the index take a +// fraction of one. +// +// Only folders of the repository that do not change while tests run are +// indexed (`INDEXED_ROOTS`). A walk or a read anywhere else (a temporary +// fixture tree a test writes and rewrites) goes to the disk every time. + +const INDEXED_ROOTS = ["src", "e2e", "prisma"].map((dir) => + resolve(process.cwd(), dir), +); + +function indexedRootOf(path: string): string | null { + const absolute = resolve(path); + return ( + INDEXED_ROOTS.find( + (root) => absolute === root || absolute.startsWith(`${root}${sep}`), + ) ?? null + ); +} + +const listingCache = new Map(); + +/** Every entry under `root`, posix-separated, sorted; memoised when indexed. */ +function listTree(root: string): string[] { + const absolute = resolve(root); + const anchor = indexedRootOf(absolute); + if (anchor === null) { + return readdirSync(absolute, { recursive: true }) + .map((entry) => String(entry).split(sep).join("/")) + .sort(); + } + let all = listingCache.get(anchor); + if (all === undefined) { + all = readdirSync(anchor, { recursive: true }) + .map((entry) => String(entry).split(sep).join("/")) + .sort(); + listingCache.set(anchor, all); + } + if (absolute === anchor) return all; + const prefix = `${relative(anchor, absolute).split(sep).join("/")}/`; + return all + .filter((rel) => rel.startsWith(prefix)) + .map((rel) => rel.slice(prefix.length)); +} + +const textCache = new Map(); + +/** + * A repository file's text, read once per worker when it lies in an indexed + * folder. Use it in place of `readFileSync(path, "utf8")` for source the + * guard reads and never writes. + */ +export function readSourceText(path: string): string { + const absolute = resolve(path); + if (indexedRootOf(absolute) === null) return readFileSync(absolute, "utf8"); + let text = textCache.get(absolute); + if (text === undefined) { + text = readFileSync(absolute, "utf8"); + textCache.set(absolute, text); + } + return text; +} + +/** + * The files under `root` (as `walkSourceFiles` lists them) whose text holds + * at least one of `markers`, with that text. The substring test runs before + * any regular expression or parse the caller applies, which is where most of + * a sweep's time used to go. The floor applies to the files walked, as in + * `walkSourceFiles`; a caller that expects matches states its own floor over + * the result. + */ +export function sourcesContaining( + root: string, + markers: string | readonly string[], + options: { + floor: number; + extensions?: readonly string[]; + include?: (rel: string) => boolean; + }, +): Array<[rel: string, text: string]> { + const wanted = typeof markers === "string" ? [markers] : markers; + const out: Array<[string, string]> = []; + for (const rel of walkSourceFiles(root, options).filter( + options.include ?? (() => true), + )) { + const text = readSourceText(join(root, rel)); + if (wanted.some((marker) => text.includes(marker))) out.push([rel, text]); + } + return out; +} export function walkSourceFiles( root: string, options: { floor: number; extensions?: readonly string[] }, ): string[] { const extensions = options.extensions ?? [".ts", ".tsx"]; - const files = readdirSync(root, { recursive: true }) - .map((entry) => String(entry).split(sep).join("/")) - .filter((rel) => extensions.some((ext) => rel.endsWith(ext))) - .sort(); + const files = listTree(root).filter((rel) => + extensions.some((ext) => rel.endsWith(ext)), + ); if (files.length < options.floor) { throw new Error( @@ -216,7 +313,7 @@ export function scanSourceMatches( const matches: SourceMatch[] = []; for (const file of files) { - const raw = readFileSync(join(root, file), "utf8"); + const raw = readSourceText(join(root, file)); const source = options.stripComments ? stripComments(raw) : raw; for (const m of source.matchAll(pattern)) { const line = source.slice(0, m.index).split("\n").length; @@ -273,7 +370,7 @@ export function readSource( `readSourceFolder and a floor.`, ); } - const text = readFileSync(path, "utf8"); + const text = readSourceText(path); const lost = list.filter((anchor) => anchorCount(text, anchor) === 0); if (lost.length > 0) { throw new Error( @@ -306,5 +403,5 @@ export function readSourceFolder( extensions: options.extensions, }).filter(options.include ?? (() => true)); requireFloor(`readSourceFolder(${relDir})`, files, options.floor); - return files.map((rel) => [rel, readFileSync(join(root, rel), "utf8")]); + return files.map((rel) => [rel, readSourceText(join(root, rel))]); } diff --git a/src/__tests__/idempotency-contract-publication.test.ts b/src/__tests__/idempotency-contract-publication.test.ts index 74af2402a..fe07fea1b 100644 --- a/src/__tests__/idempotency-contract-publication.test.ts +++ b/src/__tests__/idempotency-contract-publication.test.ts @@ -19,12 +19,13 @@ * hand-listed: the expected set is read out of the route tree on every run, so * the guard cannot fall behind the code it watches. */ -import { readFileSync, readdirSync } from "node:fs"; +import { readdirSync } from "node:fs"; import { join, relative, sep } from "node:path"; import { describe, expect, it } from "vitest"; import { buildOpenApiDocument } from "@/lib/openapi/registry"; +import { readSourceText } from "./helpers/source-files"; const API_ROOT = join(process.cwd(), "src", "app", "api"); @@ -74,7 +75,7 @@ function idempotentOperationsFromSource(): Set { const pattern = /export\s+const\s+(POST|PUT|PATCH|DELETE)\s*=\s*([\s\S]*?)(?=\n(?:export\s+const|export\s+async\s+function|$))/g; for (const file of routeFiles(API_ROOT)) { - const source = readFileSync(file, "utf8"); + const source = readSourceText(file); if (!source.includes("withIdempotency")) continue; for (const match of source.matchAll(pattern)) { if (!/\bwithIdempotency\s*[(<]/.test(match[2])) continue; diff --git a/src/__tests__/insights-cached-text-access-guard.test.ts b/src/__tests__/insights-cached-text-access-guard.test.ts index 68b95b00f..a5c71f349 100644 --- a/src/__tests__/insights-cached-text-access-guard.test.ts +++ b/src/__tests__/insights-cached-text-access-guard.test.ts @@ -12,12 +12,15 @@ * The readable name survives only as the snapshot builder's input field, which * carries the OPENED text, never the column. */ -import { readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; -import { stripComments, walkSourceFiles } from "./helpers/source-files"; +import { + readSourceText, + stripComments, + walkSourceFiles, +} from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -45,9 +48,7 @@ function sourceFilesMatching(pattern: RegExp): string[] { return walkSourceFiles(SRC, { floor: 2000 }) .filter((f) => !f.startsWith("generated/")) .filter((f) => !f.includes("__tests__/") && !/\.test\.tsx?$/.test(f)) - .filter((f) => - pattern.test(stripComments(readFileSync(join(SRC, f), "utf8"))), - ) + .filter((f) => pattern.test(stripComments(readSourceText(join(SRC, f))))) .sort(); } diff --git a/src/__tests__/latest-per-type-read-guard.test.ts b/src/__tests__/latest-per-type-read-guard.test.ts index c7c5eca17..3480b7750 100644 --- a/src/__tests__/latest-per-type-read-guard.test.ts +++ b/src/__tests__/latest-per-type-read-guard.test.ts @@ -23,11 +23,14 @@ * Comments are stripped before matching, and the planted cases prove each * matcher finds what it claims to. */ -import { readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; -import { stripComments, walkSourceFiles } from "./helpers/source-files"; +import { + readSourceText, + stripComments, + walkSourceFiles, +} from "./helpers/source-files"; /** The shared comment strip, plus SQL line comments inside template strings. */ function stripCode(text: string): string { @@ -98,7 +101,7 @@ describe("latest-per-type reads", () => { it("no production read keeps one row per type with DISTINCT ON", () => { const offenders = productionFiles().filter((file) => - LATEST_PER_TYPE.test(stripCode(readFileSync(join(SRC, file), "utf8"))), + LATEST_PER_TYPE.test(stripCode(readSourceText(join(SRC, file)))), ); expect(offenders).toEqual([]); }); @@ -129,9 +132,7 @@ describe("latest-per-type reads", () => { it("no production read groups every row for the latest per type", () => { const files = productionFiles(); const grouped = files.filter((file) => - /\.measurement\s*\.\s*groupBy\s*\(/.test( - readFileSync(join(SRC, file), "utf8"), - ), + /\.measurement\s*\.\s*groupBy\s*\(/.test(readSourceText(join(SRC, file))), ); // The tree groups measurements in a dozen places; finding none means // the walk read the wrong tree. @@ -139,16 +140,16 @@ describe("latest-per-type reads", () => { const offenders = files .filter((file) => !(file in GROUPBY_ALLOWED)) .flatMap((file) => - latestOnlyGroupBys( - stripComments(readFileSync(join(SRC, file), "utf8")), - ).map((arg) => `${file}: ${arg.slice(0, 100)}`), + latestOnlyGroupBys(stripComments(readSourceText(join(SRC, file)))).map( + (arg) => `${file}: ${arg.slice(0, 100)}`, + ), ); expect(offenders).toEqual([]); }); it("the summaries slice is built only behind its cache cell", () => { const callers = productionFiles().filter((file) => - SLICE_BUILDER_CALL.test(stripCode(readFileSync(join(SRC, file), "utf8"))), + SLICE_BUILDER_CALL.test(stripCode(readSourceText(join(SRC, file)))), ); expect(callers).toEqual(["lib/analytics/summaries-slice.ts"]); }); diff --git a/src/__tests__/linear-fit-single-implementation-guard.test.ts b/src/__tests__/linear-fit-single-implementation-guard.test.ts index 424ca3b43..ca5b7888a 100644 --- a/src/__tests__/linear-fit-single-implementation-guard.test.ts +++ b/src/__tests__/linear-fit-single-implementation-guard.test.ts @@ -7,11 +7,10 @@ * implementation. Comments are stripped first; the planted cases prove the * matcher fires. */ -import { readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -52,25 +51,21 @@ describe("one least-squares line", () => { const offenders = files.filter( (file) => !(file in ALLOWED) && - HAND_ROLLED_SLOPE.test( - stripComments(readFileSync(join(SRC, file), "utf8")), - ), + HAND_ROLLED_SLOPE.test(stripComments(readSourceText(join(SRC, file)))), ); expect(offenders).toEqual([]); // The six readers now call the implementation: a sweep that finds fewer // call sites read the wrong tree or lost one. const callers = files.filter((file) => /\b(?:linearFit|fitFromSums|trendLineByTime)\s*\(/.test( - stripComments(readFileSync(join(SRC, file), "utf8")), + stripComments(readSourceText(join(SRC, file))), ), ); expect(callers.length).toBeGreaterThanOrEqual(7); // The implementation itself matches, so the sweep read real files. expect( HAND_ROLLED_SLOPE.test( - stripComments( - readFileSync(join(SRC, "lib/stats/linear-fit.ts"), "utf8"), - ), + stripComments(readSourceText(join(SRC, "lib/stats/linear-fit.ts"))), ), ).toBe(true); }); diff --git a/src/__tests__/memory-block-egress-guard.test.ts b/src/__tests__/memory-block-egress-guard.test.ts index a874be864..7c0fa8bd7 100644 --- a/src/__tests__/memory-block-egress-guard.test.ts +++ b/src/__tests__/memory-block-egress-guard.test.ts @@ -18,18 +18,17 @@ * * Every check is also run against a source broken on purpose. */ -import { readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); const MEMORY = "lib/ai/coach/memory"; function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } function sourceFiles(): string[] { diff --git a/src/__tests__/naive-timestamp-zone-shift-guard.test.ts b/src/__tests__/naive-timestamp-zone-shift-guard.test.ts index 1ae978874..e9c0a5642 100644 --- a/src/__tests__/naive-timestamp-zone-shift-guard.test.ts +++ b/src/__tests__/naive-timestamp-zone-shift-guard.test.ts @@ -17,11 +17,14 @@ * (`bucket_start`) is out of its reach by name. Comments are stripped first; * the planted cases prove it fires. It is a tripwire, not a proof. */ -import { readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; -import { stripComments, walkSourceFiles } from "./helpers/source-files"; +import { + readSourceText, + stripComments, + walkSourceFiles, +} from "./helpers/source-files"; /** The shared comment strip, plus SQL line comments inside template strings. */ function stripCode(text: string): string { @@ -59,8 +62,7 @@ describe("naive timestamp zone shifts", () => { !file.includes("__tests__/") && !/\.test\.tsx?$/.test(file), ); - const code = (file: string) => - stripCode(readFileSync(join(SRC, file), "utf8")); + const code = (file: string) => stripCode(readSourceText(join(SRC, file))); // The reads that shift correctly, so a sweep that reads nothing fails. const doubleShifts = files.filter((file) => DOUBLE_SHIFT.test(code(file))); expect(doubleShifts.length).toBeGreaterThanOrEqual(5); diff --git a/src/__tests__/no-control-bytes-guard.test.ts b/src/__tests__/no-control-bytes-guard.test.ts index aefe05080..f9368aeae 100644 --- a/src/__tests__/no-control-bytes-guard.test.ts +++ b/src/__tests__/no-control-bytes-guard.test.ts @@ -1,9 +1,9 @@ -import { readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; import { walkSourceFiles } from "@/__tests__/helpers/source-files"; +import { readSourceText } from "./helpers/source-files"; /** * No raw control byte in a source file, apart from tab, newline and carriage @@ -28,7 +28,7 @@ describe("source files carry no raw control bytes", () => { expect(files.length).toBeGreaterThan(3000); const offenders: string[] = []; for (const file of files) { - const lines = readFileSync(join(src, file), "utf8").split("\n"); + const lines = readSourceText(join(src, file)).split("\n"); lines.forEach((line, i) => { if (CONTROL.test(line)) offenders.push(`${file}:${i + 1}`); }); diff --git a/src/__tests__/oauth-kit-guard.test.ts b/src/__tests__/oauth-kit-guard.test.ts index a5e2ca106..4021ebb08 100644 --- a/src/__tests__/oauth-kit-guard.test.ts +++ b/src/__tests__/oauth-kit-guard.test.ts @@ -26,10 +26,10 @@ * that stops matching fails instead of passing. */ import { describe, expect, it } from "vitest"; -import { existsSync, readFileSync } from "node:fs"; +import { existsSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -54,7 +54,7 @@ function productionFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(process.cwd(), rel), "utf8"); + return readSourceText(join(process.cwd(), rel)); } describe("OAuth token lifecycle guard", () => { diff --git a/src/__tests__/profile-rejection-surface-guard.test.ts b/src/__tests__/profile-rejection-surface-guard.test.ts index e0b12d002..04aaaa894 100644 --- a/src/__tests__/profile-rejection-surface-guard.test.ts +++ b/src/__tests__/profile-rejection-surface-guard.test.ts @@ -20,10 +20,9 @@ * frozen so that adding one has to be a deliberate edit here. */ import { describe, it, expect } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -47,7 +46,7 @@ function sourceFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } /** diff --git a/src/__tests__/recent-proof-surface-guard.test.ts b/src/__tests__/recent-proof-surface-guard.test.ts index 4c818ed17..9e1838b78 100644 --- a/src/__tests__/recent-proof-surface-guard.test.ts +++ b/src/__tests__/recent-proof-surface-guard.test.ts @@ -14,15 +14,14 @@ * with every list. */ import { describe, it, expect } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } function sourceFiles(): string[] { diff --git a/src/__tests__/record-session-fence-guard.test.ts b/src/__tests__/record-session-fence-guard.test.ts index 9dc76eede..e6cacd6a0 100644 --- a/src/__tests__/record-session-fence-guard.test.ts +++ b/src/__tests__/record-session-fence-guard.test.ts @@ -39,10 +39,9 @@ * shipped several of those. */ import { describe, it, expect } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -55,7 +54,7 @@ function sourceFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } function filesMatching(re: RegExp): string[] { diff --git a/src/__tests__/recurrence-course-anchor-guard.test.ts b/src/__tests__/recurrence-course-anchor-guard.test.ts index d41f7bb95..c7e9fd1ca 100644 --- a/src/__tests__/recurrence-course-anchor-guard.test.ts +++ b/src/__tests__/recurrence-course-anchor-guard.test.ts @@ -19,10 +19,11 @@ * `RecurrenceContext` annotation is invisible here. Every medication path * today goes through one of the annotated builders below. */ -import { readdirSync, readFileSync } from "node:fs"; +import { readdirSync } from "node:fs"; import { join, relative, resolve } from "node:path"; import { describe, expect, it } from "vitest"; +import { readSourceText } from "./helpers/source-files"; const SRC = resolve(__dirname, ".."); const ROOT = resolve(SRC, ".."); @@ -73,7 +74,7 @@ interface Builder { function builders(): Builder[] { const out: Builder[] = []; for (const file of sourceFiles(SRC)) { - const text = readFileSync(file, "utf8"); + const text = readSourceText(file); for (const m of text.matchAll(DECLARATION)) { const open = m.index! + m[0].length - 1; out.push({ diff --git a/src/__tests__/session-surface-guard.test.ts b/src/__tests__/session-surface-guard.test.ts index 4916b1cee..c97358181 100644 --- a/src/__tests__/session-surface-guard.test.ts +++ b/src/__tests__/session-surface-guard.test.ts @@ -33,10 +33,9 @@ * matches nothing fails instead of passing. */ import { describe, it, expect } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -55,7 +54,7 @@ function appFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } function appFilesMatching(re: RegExp): string[] { diff --git a/src/__tests__/sharing-surface-guard.test.ts b/src/__tests__/sharing-surface-guard.test.ts index fe252a19d..b818a899f 100644 --- a/src/__tests__/sharing-surface-guard.test.ts +++ b/src/__tests__/sharing-surface-guard.test.ts @@ -65,7 +65,7 @@ * different set of symbols and answers it from the AST, because a route * explaining in a comment why it is NOT delegable is a thing worth writing. */ -import { readFileSync, readdirSync } from "node:fs"; +import { readdirSync } from "node:fs"; import { join, sep } from "node:path"; import ts from "typescript"; @@ -75,6 +75,7 @@ import { ENTIRE_RECORD, SHARE_DOMAINS } from "@/lib/sharing/scope"; import type { ShareScope } from "@/lib/sharing/scope"; import { ADMITTED_MUTATING_HANDLERS } from "../../tests/fixtures/v137/sharing-matrix"; +import { readSourceText } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -198,7 +199,7 @@ const textCache = new Map(); function read(rel: string): string { const cached = textCache.get(rel); if (cached !== undefined) return cached; - const text = readFileSync(join(SRC, rel), "utf8"); + const text = readSourceText(join(SRC, rel)); textCache.set(rel, text); return text; } diff --git a/src/__tests__/spread-single-implementation-guard.test.ts b/src/__tests__/spread-single-implementation-guard.test.ts index da69c33f5..815f3cb9c 100644 --- a/src/__tests__/spread-single-implementation-guard.test.ts +++ b/src/__tests__/spread-single-implementation-guard.test.ts @@ -16,11 +16,14 @@ * `glucoseSD`) and is out of the matcher's reach by shape. Comments are * stripped first; the planted case proves the matcher fires. */ -import { readFileSync } from "node:fs"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; -import { stripComments, walkSourceFiles } from "./helpers/source-files"; +import { + readSourceText, + stripComments, + walkSourceFiles, +} from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -51,7 +54,7 @@ function computesSpread(code: string): boolean { } function read(file: string): string { - return stripComments(readFileSync(join(SRC, file), "utf8")); + return stripComments(readSourceText(join(SRC, file))); } describe("one standard deviation", () => { diff --git a/src/__tests__/sql-now-utc-guard.test.ts b/src/__tests__/sql-now-utc-guard.test.ts index 00df946cb..096f85055 100644 --- a/src/__tests__/sql-now-utc-guard.test.ts +++ b/src/__tests__/sql-now-utc-guard.test.ts @@ -13,9 +13,10 @@ * The few bare reads left are listed below with the reason they are safe: * each feeds a `timestamptz` column or returns the instant to JavaScript. */ -import { readdirSync, readFileSync, statSync } from "node:fs"; +import { readdirSync, statSync } from "node:fs"; import { join, relative } from "node:path"; import { describe, expect, it } from "vitest"; +import { readSourceText } from "./helpers/source-files"; const ROOT = join(__dirname, "..", ".."); const SRC = join(ROOT, "src"); @@ -69,7 +70,7 @@ function bareReads(): string[] { const found: string[] = []; for (const file of walk(SRC)) { const rel = relative(ROOT, file); - readFileSync(file, "utf8") + readSourceText(file) .split("\n") .forEach((line, index) => { if (/^\s*(\/\/|\*|\/\*|--)/.test(line)) return; @@ -84,7 +85,7 @@ function bareReads(): string[] { describe("raw SQL clock reads are anchored to UTC", () => { it("finds the anchored form across the swept files", () => { // The matcher must see something, or a broken walk would pass. - const passkey = readFileSync(join(SRC, "lib/auth/passkey.ts"), "utf8"); + const passkey = readSourceText(join(SRC, "lib/auth/passkey.ts")); expect(passkey.match(ANCHORED)?.length).toBeGreaterThanOrEqual(3); expect(SQL_CLOCK.test("AND expires_at > NOW()")).toBe(true); expect(SQL_CLOCK.test("updated_at = now()")).toBe(true); diff --git a/src/__tests__/step-up-elevation-guard.test.ts b/src/__tests__/step-up-elevation-guard.test.ts index 366abc900..e295dba3b 100644 --- a/src/__tests__/step-up-elevation-guard.test.ts +++ b/src/__tests__/step-up-elevation-guard.test.ts @@ -23,10 +23,9 @@ * only that it has not changed without someone editing this file. */ import { describe, it, expect } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -39,7 +38,7 @@ function sourceFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } /** Local names bound for `exportName` from any module matching `moduleRe`. */ diff --git a/src/__tests__/vorsorge-due-bucket-single-home-guard.test.ts b/src/__tests__/vorsorge-due-bucket-single-home-guard.test.ts index 2e94964d1..1539b8772 100644 --- a/src/__tests__/vorsorge-due-bucket-single-home-guard.test.ts +++ b/src/__tests__/vorsorge-due-bucket-single-home-guard.test.ts @@ -18,10 +18,9 @@ * settles is that there is only one of it. */ import { describe, it, expect } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -81,7 +80,7 @@ function sourceFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } /** A line that declares `relativeDueKey`, ignoring comment prose. */ diff --git a/src/__tests__/workout-sport-provenance-guard.test.ts b/src/__tests__/workout-sport-provenance-guard.test.ts index e44e79747..280c7fe55 100644 --- a/src/__tests__/workout-sport-provenance-guard.test.ts +++ b/src/__tests__/workout-sport-provenance-guard.test.ts @@ -19,10 +19,9 @@ * that a new ingest cannot appear without someone editing this file. */ import { describe, it, expect } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -36,7 +35,7 @@ function sourceFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } interface ProviderIngest { From 743a1e742fb11dc33fbe42cda229a3fe7c718f09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Thu, 22 Oct 2026 10:30:59 +0200 Subject: [PATCH 3/3] test(guards): stop the slowest guards repeating their own work Each of these asked the same question several times or did the expensive part before the cheap one: the threshold guard parsed every client file once per rule and twice per run, the i18n reverse check rebuilt its scan three times and compared every leaf with every literal key, the step-up caller guard stripped comments and searched every route path in every file per case, the plausibility guard ran its writer sweep three times, and the cycle verdict guard parsed every client file to find out whether it mentions a cycle. Each now works a result out once, and a file is parsed or searched only when a substring says it can match. A planted violation still fails the threshold, plausibility, cycle verdict and backup plan guards. --- .../backup-plan-classification.test.ts | 16 ++++++ .../client-threshold-literal-guard.test.ts | 56 +++++++++++++++---- ...cycle-verdict-single-decider-guard.test.ts | 24 ++++---- .../helpers/cycle-verdict-derivation.ts | 12 +++- src/__tests__/helpers/i18n-reference.ts | 18 ++++-- src/__tests__/i18n-reverse-coverage.test.ts | 15 ++++- ...asurement-write-plausibility-guard.test.ts | 25 ++++++--- .../step-up-web-caller-guard.test.ts | 34 ++++++++--- 8 files changed, 153 insertions(+), 47 deletions(-) diff --git a/src/__tests__/backup-plan-classification.test.ts b/src/__tests__/backup-plan-classification.test.ts index 9151ef24f..648ccae7d 100644 --- a/src/__tests__/backup-plan-classification.test.ts +++ b/src/__tests__/backup-plan-classification.test.ts @@ -271,10 +271,17 @@ function delegateName(model: string): string { * route outright: the guard stayed green on all twelve checks, because the * cycle file was answering a question about a different table. */ +const parentsCache = new Map< + string, + Array<{ field: string; parent: string }> +>(); + function relationParents(model: string): Array<{ field: string; parent: string; }> { + const cached = parentsCache.get(model); + if (cached) return cached; const found: Array<{ field: string; parent: string }> = []; for (const [parent, body] of modelBodies()) { for (const line of body.split("\n")) { @@ -284,6 +291,7 @@ function relationParents(model: string): Array<{ } } } + parentsCache.set(model, found); return found; } @@ -295,6 +303,8 @@ const WRITE_RELATION_OPS = /\{\s*create\b/; /** Does `source` call a matching operation on `model`'s own Prisma delegate? */ function callsDelegate(source: string, model: string, ops: RegExp): boolean { const delegate = delegateName(model); + // The expression below names the delegate; a file without it cannot match. + if (!source.includes(delegate)) return false; for (const match of source.matchAll( new RegExp(`\\.${delegate}\\s*\\.\\s*(\\w+)`, "g"), )) { @@ -304,7 +314,11 @@ function callsDelegate(source: string, model: string, ops: RegExp): boolean { } /** Every model a relation field of this name points at, across the schema. */ +const targetsCache = new Map>(); + function targetsOfRelationField(field: string): Set { + const cached = targetsCache.get(field); + if (cached) return cached; const targets = new Set(); for (const [, body] of modelBodies()) { for (const line of body.split("\n")) { @@ -312,6 +326,7 @@ function targetsOfRelationField(field: string): Set { if (match && match[1] === field) targets.add(match[2]); } } + targetsCache.set(field, targets); return targets; } @@ -337,6 +352,7 @@ function touches( const source = flat(file); if (callsDelegate(source, model, delegateOps)) return true; return relationParents(model).some(({ field, parent }) => { + if (!source.includes(field)) return false; if ( !new RegExp(`\\b${field}\\s*:\\s*(?:${relationOps.source})`).test( source, diff --git a/src/__tests__/client-threshold-literal-guard.test.ts b/src/__tests__/client-threshold-literal-guard.test.ts index 3ac062f5e..cbce6d565 100644 --- a/src/__tests__/client-threshold-literal-guard.test.ts +++ b/src/__tests__/client-threshold-literal-guard.test.ts @@ -33,13 +33,16 @@ * from a variable that happens to hold a literal is not seen; review catches * that. */ -import { readFileSync } from "node:fs"; import { join } from "node:path"; import ts from "typescript"; import { describe, expect, it } from "vitest"; -import { stripComments, walkSourceFiles } from "./helpers/source-files"; +import { + readSourceText, + stripComments, + walkSourceFiles, +} from "./helpers/source-files"; const ROOT = process.cwd(); @@ -67,7 +70,7 @@ function clientFiles(): ClientFile[] { const rel = `${dir}/${file}`; if (rel.startsWith("src/app/api/")) continue; if (rel.includes("/__tests__/") || /\.test\.tsx?$/.test(rel)) continue; - const source = readFileSync(join(ROOT, rel), "utf8"); + const source = readSourceText(join(ROOT, rel)); if (!isClientModule(rel, source)) continue; out.push({ rel, source }); } @@ -75,14 +78,23 @@ function clientFiles(): ClientFile[] { return out; } +const parsed = new Map(); + +/** One parse per file and source, however many rules read it. */ function parse(name: string, source: string): ts.SourceFile { - return ts.createSourceFile( - name, - source, - ts.ScriptTarget.Latest, - true, - name.endsWith("x") ? ts.ScriptKind.TSX : ts.ScriptKind.TS, - ); + const key = `${name}\0${source}`; + let file = parsed.get(key); + if (file === undefined) { + file = ts.createSourceFile( + name, + source, + ts.ScriptTarget.Latest, + true, + name.endsWith("x") ? ts.ScriptKind.TSX : ts.ScriptKind.TS, + ); + parsed.set(key, file); + } + return file; } /** The literal text a JSX attribute initialiser carries, if it is one. */ @@ -218,6 +230,11 @@ function resolverImports(name: string, source: string): string[] { interface Rule { name: string; find: (name: string, source: string) => string[]; + /** + * Text a file must contain for `find` to have anything to report, tested + * before any parse. Every shape the rule matches carries one of these. + */ + markers: readonly string[]; /** Sources that must produce at least one hit, proving the matcher. */ samples: Array<{ name: string; source: string }>; /** File → exact hit count it may have, with the reason. */ @@ -228,6 +245,7 @@ const RULES: Rule[] = [ { name: "a literal directionSentiment", find: literalDirections, + markers: ["directionSentiment"], samples: [ { name: "sample.tsx", @@ -249,6 +267,7 @@ const RULES: Rule[] = [ { name: "a reference line at a literal", find: literalLines, + markers: [...LINE_TAGS], samples: [ { name: "sample.tsx", @@ -280,12 +299,14 @@ const RULES: Rule[] = [ { name: "a polarity table", find: polarityTables, + markers: ["up-good", "up-bad", "UP_GOOD_", "UP_BAD_", "Sentiment"], samples: POLARITY_SAMPLES.map((source) => ({ name: "sample.ts", source })), allow: {}, }, { name: "a server resolver imported into the browser", find: resolverImports, + markers: SERVER_RESOLVERS, samples: [ { name: "sample.ts", @@ -299,12 +320,19 @@ const RULES: Rule[] = [ const FILES = clientFiles(); +const ruleHits = new Map>(); + +/** Each rule's hits across the client tree, found once per run. */ function hitsFor(rule: Rule): Map { - const out = new Map(); + let out = ruleHits.get(rule); + if (out !== undefined) return out; + out = new Map(); for (const { rel, source } of FILES) { + if (!rule.markers.some((marker) => source.includes(marker))) continue; const hits = rule.find(rel, source); if (hits.length > 0) out.set(rel, hits); } + ruleHits.set(rule, out); return out; } @@ -321,6 +349,12 @@ describe("client threshold literal guard", () => { rule.find(sample.name, sample.source).length, `no hit on: ${sample.source}`, ).toBeGreaterThan(0); + // The pre-filter has to let every sample through, or it would hide + // the shape before the matcher saw it. + expect( + rule.markers.some((marker) => sample.source.includes(marker)), + `the markers skip: ${sample.source}`, + ).toBe(true); } }); diff --git a/src/__tests__/cycle-verdict-single-decider-guard.test.ts b/src/__tests__/cycle-verdict-single-decider-guard.test.ts index 4214e2a0d..48a19d200 100644 --- a/src/__tests__/cycle-verdict-single-decider-guard.test.ts +++ b/src/__tests__/cycle-verdict-single-decider-guard.test.ts @@ -13,7 +13,7 @@ * nothing in the client tree may derive the verdict, AND the surfaces that * show it must still be reading the published field. */ -import { readFileSync, readdirSync, statSync } from "node:fs"; +import { readdirSync, statSync } from "node:fs"; import { join, relative as relativeTo } from "node:path"; import { describe, expect, it } from "vitest"; @@ -21,8 +21,10 @@ import { describe, expect, it } from "vitest"; import { accessPaths, derivationFindings, + mayBeCycleContext, parse, } from "./helpers/cycle-verdict-derivation"; +import { readSourceText } from "./helpers/source-files"; const ROOT = process.cwd(); @@ -61,7 +63,7 @@ function clientFiles(): string[] { } function parseFile(relative: string) { - return parse(relative, readFileSync(join(ROOT, relative), "utf8")); + return parse(relative, readSourceText(join(ROOT, relative))); } describe("the cycle verdict is decided in one place", () => { @@ -70,9 +72,11 @@ describe("the cycle verdict is decided in one place", () => { // A guard over an empty file list proves nothing. expect(files.length).toBeGreaterThan(100); - const findings = files.flatMap((relative) => - derivationFindings(parseFile(relative)), - ); + const findings = files + .filter((relative) => + mayBeCycleContext(readSourceText(join(ROOT, relative))), + ) + .flatMap((relative) => derivationFindings(parseFile(relative))); expect( findings.map((f) => `${f.file}:${f.line} ${f.kind} — ${f.where}`), @@ -83,7 +87,7 @@ describe("the cycle verdict is decided in one place", () => { // The specific constant that was copied. Named here so its return is loud // rather than merely structural. const offenders = clientFiles().filter((relative) => - readFileSync(join(ROOT, relative), "utf8").includes("OVERDUE_GRACE_DAYS"), + readSourceText(join(ROOT, relative)).includes("OVERDUE_GRACE_DAYS"), ); expect(offenders).toEqual([]); }); @@ -106,9 +110,8 @@ describe("the surfaces that show the verdict actually read it", () => { // The BBT chart used to walk the calendar itself to find the cycle start, // which was a second copy of the same walk. It now takes the resolved // value as a prop, and the cycle page feeds it from the verdict. - const chart = readFileSync( + const chart = readSourceText( join(ROOT, "src/components/cycle/bbt-chart.tsx"), - "utf8", ); expect(chart).toContain("cycleStartDate"); @@ -119,10 +122,7 @@ describe("the surfaces that show the verdict actually read it", () => { it("the calendar response type declares the verdict as always present", () => { // An optional verdict is a plausible default that lets a client fall back // to deriving its own. - const types = readFileSync( - join(ROOT, "src/components/cycle/types.ts"), - "utf8", - ); + const types = readSourceText(join(ROOT, "src/components/cycle/types.ts")); expect(types).toContain("verdict: CycleVerdict;"); expect(types).not.toContain("verdict?:"); }); diff --git a/src/__tests__/helpers/cycle-verdict-derivation.ts b/src/__tests__/helpers/cycle-verdict-derivation.ts index c15f1af59..df60342c7 100644 --- a/src/__tests__/helpers/cycle-verdict-derivation.ts +++ b/src/__tests__/helpers/cycle-verdict-derivation.ts @@ -51,7 +51,7 @@ const DAY_COUNT_NAME = /day.?of.?cycle|cycleday|cyclerun|runlength/i; * calendar's types, its phase names, or the verdict's own fields, so nothing * that could actually commit the defect escapes by renaming one identifier. */ -const CYCLE_MARKERS = new Set([ +export const CYCLE_MARKERS: ReadonlySet = new Set([ "CalendarDay", "CalendarResponse", "CyclePhase", @@ -247,6 +247,16 @@ function isSumLike( return false; } +/** + * Whether a file's text could speak the cycle's vocabulary at all: every + * marker the parse below looks for is also a substring of the text, so a + * file holding none of them needs no parse. + */ +export function mayBeCycleContext(text: string): boolean { + for (const marker of CYCLE_MARKERS) if (text.includes(marker)) return true; + return false; +} + /** Whether the file speaks the cycle's vocabulary at all. */ export function isCycleContext(file: ts.SourceFile): boolean { let found = false; diff --git a/src/__tests__/helpers/i18n-reference.ts b/src/__tests__/helpers/i18n-reference.ts index b376adf92..642d8b27c 100644 --- a/src/__tests__/helpers/i18n-reference.ts +++ b/src/__tests__/helpers/i18n-reference.ts @@ -305,11 +305,19 @@ function suffixBaseMatches(base: string, ref: Reference): boolean { export function isReferenced(leaf: string, ref: Reference): boolean { if (ref.literalKeys.has(leaf)) return true; - for (const ck of ref.literalKeys) { - if (leaf.startsWith(`${ck}.`)) return true; - } - for (const base of ref.dottedBases) { - if (leaf === base || leaf.startsWith(`${base}.`)) return true; + // A literal key or a dotted base covers `leaf` when it IS one of the + // leaf's dotted ancestors (or, for a base, the leaf itself), so the leaf's + // own ancestors are looked up rather than every key in the sets scanned. + if (ref.dottedBases.has(leaf)) return true; + for ( + let dot = leaf.indexOf("."); + dot !== -1; + dot = leaf.indexOf(".", dot + 1) + ) { + const ancestor = leaf.slice(0, dot); + if (ref.literalKeys.has(ancestor) || ref.dottedBases.has(ancestor)) { + return true; + } } for (const pre of ref.concatPrefixes) { if (leaf.startsWith(pre)) return true; diff --git a/src/__tests__/i18n-reverse-coverage.test.ts b/src/__tests__/i18n-reverse-coverage.test.ts index 256bcbd89..01f24c556 100644 --- a/src/__tests__/i18n-reverse-coverage.test.ts +++ b/src/__tests__/i18n-reverse-coverage.test.ts @@ -26,10 +26,11 @@ * its keys report as orphans. */ import { describe, it, expect } from "vitest"; -import { readFileSync, readdirSync, statSync } from "node:fs"; +import { readdirSync, statSync } from "node:fs"; import { join } from "node:path"; import { collectReferences, isReferenced } from "./helpers/i18n-reference"; +import { readSourceText } from "./helpers/source-files"; const ROOT = join(__dirname, "../.."); const SRC = join(ROOT, "src"); @@ -142,8 +143,16 @@ function isAllowlisted(leaf: string): boolean { ); } +let scanned: ReturnType | null = null; + +/** The bundle and the tree's references, worked out once per run. */ function scan() { - const en = JSON.parse(readFileSync(EN_BUNDLE_PATH, "utf8")); + scanned ??= scanOnce(); + return scanned; +} + +function scanOnce() { + const en = JSON.parse(readSourceText(EN_BUNDLE_PATH)); const leaves: string[] = []; leafPaths(en, "", leaves); @@ -152,7 +161,7 @@ function scan() { const topNamespaces = new Set(Object.keys(en)); const allText = walk(SRC) - .map((f) => readFileSync(f, "utf8")) + .map((f) => readSourceText(f)) .join("\n"); return { leaves, ...collectReferences(allText, allNodes, topNamespaces) }; diff --git a/src/__tests__/measurement-write-plausibility-guard.test.ts b/src/__tests__/measurement-write-plausibility-guard.test.ts index 386c58703..d7a6d35a1 100644 --- a/src/__tests__/measurement-write-plausibility-guard.test.ts +++ b/src/__tests__/measurement-write-plausibility-guard.test.ts @@ -19,10 +19,9 @@ * test substitutes for that review. */ import { describe, it, expect } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); @@ -164,14 +163,25 @@ function sourceFiles(): string[] { } function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } +let writers: string[] | null = null; + +/** + * Every file that writes a measurement, found once per run. A write names + * the `measurement` delegate or the bulk insert's `MeasurementRows`, so a + * file holding neither word is skipped before the expressions run. + */ function discoveredWriters(): string[] { - return sourceFiles().filter((rel) => { + writers ??= sourceFiles().filter((rel) => { const text = read(rel); + if (!text.includes("measurement") && !text.includes("MeasurementRows")) { + return false; + } return MEASUREMENT_WRITE.test(text) || BULK_INSERT_CALL.test(text); }); + return writers; } describe("every measurement writer declares how it meets the plausibility domain", () => { @@ -195,9 +205,10 @@ describe("every measurement writer declares how it meets the plausibility domain }); it("raw SQL inserts measurements in the bulk-insert module and nowhere else", () => { - const raw = sourceFiles().filter((rel) => - RAW_MEASUREMENT_INSERT.test(read(rel)), - ); + const raw = sourceFiles().filter((rel) => { + const text = read(rel); + return /insert/i.test(text) && RAW_MEASUREMENT_INSERT.test(text); + }); expect(raw).toEqual([BULK_INSERT_MODULE]); }); diff --git a/src/__tests__/step-up-web-caller-guard.test.ts b/src/__tests__/step-up-web-caller-guard.test.ts index b6f1a8d76..0e9e3646e 100644 --- a/src/__tests__/step-up-web-caller-guard.test.ts +++ b/src/__tests__/step-up-web-caller-guard.test.ts @@ -32,27 +32,33 @@ * with every list. */ import { describe, expect, it } from "vitest"; -import { readFileSync } from "node:fs"; import { join } from "node:path"; -import { walkSourceFiles } from "./helpers/source-files"; +import { readSourceText, walkSourceFiles } from "./helpers/source-files"; const SRC = join(process.cwd(), "src"); function read(rel: string): string { - return readFileSync(join(SRC, rel), "utf8"); + return readSourceText(join(SRC, rel)); } /** * Source with comments removed, so a route that only DESCRIBES a gate in its * doc comment is not counted, and a path quoted in a comment is not a call. */ +const codeCache = new Map(); + function code(rel: string): string { - return read(rel) - .replace(/\/\*[\s\S]*?\*\//g, "") - .split("\n") - .map((line) => line.replace(/(^|[^:"'`\\])\/\/.*$/, "$1")) - .join("\n"); + let stripped = codeCache.get(rel); + if (stripped === undefined) { + stripped = read(rel) + .replace(/\/\*[\s\S]*?\*\//g, "") + .split("\n") + .map((line) => line.replace(/(^|[^:"'`\\])\/\/.*$/, "$1")) + .join("\n"); + codeCache.set(rel, stripped); + } + return stripped; } const METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"] as const; @@ -252,7 +258,15 @@ function hookCallersWrap( return callers > 0; } +let gatedCalls: GatedCall[] | null = null; + +/** Every web call of a gated route, found once per run. */ function gatedWebCalls(): GatedCall[] { + gatedCalls ??= findGatedWebCalls(); + return gatedCalls; +} + +function findGatedWebCalls(): GatedCall[] { const calls: GatedCall[] = []; const files = webFiles(); const sources = new Map(files.map((f) => [f, code(f)])); @@ -260,8 +274,12 @@ function gatedWebCalls(): GatedCall[] { if (GATED_ROUTES[route] !== "web") continue; const [method, path] = route.split(" "); const re = pathPattern(path); + // Every match spells the path's static head out literally, up to the + // first `[param]`; a file without it cannot match and is not searched. + const head = path.split("/[")[0]; for (const file of files) { const src = sources.get(file) ?? ""; + if (!src.includes(head)) continue; for (const m of src.matchAll(re)) { const index = m.index ?? 0; // The path handed to a component as a prop (`endpoint="/api/…"`):