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] 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 {