import { readFileSync } from "node:fs"; import { fileURLToPath } from "node:url"; import { ESLint, type Linter } from "eslint"; import prettierConfig from "eslint-config-prettier"; import ts from "typescript"; import tseslint from "typescript-eslint"; import { describe, expect, it } from "vitest"; // The gate register (docs/gates.md) is held to the EFFECTIVE configuration (E32). A narrowing that // is not on the register fails here — a rule switched off for a scope, a weak compiler flag, a ruff // ignore, a knip exclusion, an accepted CVE. So "the gate checks X" is a claim this test verifies, // not one a comment makes. (The comment that claimed the React Compiler's rules "are also linted" // stood for months while the rule that would have said so was off.) // // Each narrowing is a (scope, id) pair; the register must hold a table row whose first cell is the // scope and which names the id in backticks. const REPO = fileURLToPath(new URL("../../../", import.meta.url)); const FRONTEND = `${REPO}frontend/`; const read = (path: string) => readFileSync(REPO + path, "utf8"); /** register scope → every backticked id on its rows. */ function register(): Map> { const rows = new Map>(); for (const line of read("docs/gates.md").split("\n")) { if (!line.startsWith("|") || /^\|(\s*-+\s*\|)+\s*$/.test(line)) continue; // not the |---| rule const scope = line.split("|")[1]!.trim(); const ids = rows.get(scope) ?? new Set(); for (const m of line.matchAll(/`([^`]+)`/g)) ids.add(m[1]!); rows.set(scope, ids); } return rows; } function missingFrom(found: [scope: string, id: string][]): string[] { const reg = register(); return found.filter(([scope, id]) => !reg.get(scope)?.has(id)).map(([s, id]) => `${s}: ${id}`); } const jsonc = (path: string) => { const { config, error } = ts.parseConfigFileTextToJson(path, read(path)); if (error) throw new Error(`${path}: ${ts.flattenDiagnosticMessageText(error.messageText, "\n")}`); return config as Record; }; // --- ESLint --------------------------------------------------------------------------------------- type Rules = Partial; const severity = (v: Linter.RuleEntry | undefined): number => { if (v === undefined) return -1; // not applied at all const s = Array.isArray(v) ? v[0] : v; return s === 2 || s === "error" ? 2 : s === 1 || s === "warn" ? 1 : 0; }; // Formatting is Prettier's (its own lane): the rules eslint-config-prettier switches off are not a // narrowing of anything. const PRETTIER_OFF = new Set( Object.entries(prettierConfig.rules ?? {}) .filter(([, v]) => severity(v as Linter.RuleEntry) === 0) .map(([r]) => r), ); // …and the core rules typescript-eslint's compatibility layer switches off for TS files because the // type checker reports them itself (no-undef, no-redeclare, no-dupe-keys, …). const TSC_OWNED = new Set( Object.entries(tseslint.configs.eslintRecommended.rules ?? {}) .filter(([, v]) => severity(v as Linter.RuleEntry) === 0) .map(([r]) => r), ); const family = (rule: string) => (rule.includes("/") ? `${rule.split("/")[0]}/*` : rule); const SCOPES: Record = { src: "src/components/Feed.tsx", "src tests": "src/components/Feed.ytsort.dom.test.tsx", "FocusSafeButton.tsx": "src/components/ui/FocusSafeButton.tsx", "AnchoredPopover.tsx": "src/components/ui/AnchoredPopover.tsx", "design-lab": "src/design-lab/batchA.tsx", e2e: "e2e/nav.spec.ts", "ts configs": "vite.config.ts", "js configs": "eslint.config.js", }; async function eslintNarrowings(): Promise<[string, string][]> { const eslint = new ESLint({ cwd: FRONTEND }); const rules: Record = {}; for (const [scope, file] of Object.entries(SCOPES)) rules[scope] = ((await eslint.calculateConfigForFile(file)) as Linter.Config).rules ?? {}; const src = rules.src!; const ignorable = (rule: string, all: Rules) => PRETTIER_OFF.has(rule) || TSC_OWNED.has(rule) || // a core rule off because its typescript-eslint twin replaces it is reported as the twin (!rule.includes("/") && `@typescript-eslint/${rule}` in all); const found: [string, string][] = []; // src: everything at off or warn for (const [rule, v] of Object.entries(src)) if (severity(v) < 2 && !ignorable(rule, src)) found.push(["src", rule]); // every other scope: what is WEAKER there than in src (a rule family not applied at all is one id) for (const scope of Object.keys(SCOPES).filter((s) => s !== "src")) { const ids = new Set(); for (const [rule, v] of Object.entries(src)) { if (ignorable(rule, src) || severity(v) < 2) continue; const there = severity(rules[scope]![rule]); if (there < 2 && !(there === 1 && severity(v) === 1)) ids.add(there < 0 ? family(rule) : rule); } for (const id of ids) found.push([scope, id]); } for (const ignored of eslintIgnores()) found.push(["everywhere", ignored]); return found; } /** The global `ignores` of eslint.config.js (the first, file-less config object). */ function eslintIgnores(): string[] { const m = /\{\s*ignores:\s*\[([^\]]*)\]\s*\}/.exec(read("frontend/eslint.config.js")); return m ? [...m[1]!.matchAll(/"([^"]+)"/g)].map((x) => x[1]!) : []; } describe("the gate register (docs/gates.md)", () => { it("lists every ESLint rule narrowed, by scope", async () => { expect(missingFrom(await eslintNarrowings())).toEqual([]); }, 60_000); it("keeps --max-warnings 0, which is what makes a warn-level rule fail", () => { const pkg = JSON.parse(read("frontend/package.json")) as { scripts: Record }; expect(pkg.scripts.lint).toMatch(/--max-warnings 0\b/); }); // --- TypeScript --------------------------------------------------------------------------------- const PROJECTS = [ "frontend/tsconfig.json", "frontend/tsconfig.node.json", "frontend/e2e/tsconfig.json", ]; const REQUIRED_ON = [ "strict", "noUncheckedIndexedAccess", "noUnusedLocals", "noUnusedParameters", "noFallthroughCasesInSwitch", ]; it.each(PROJECTS)("%s keeps the strict flags on", (path) => { const opts = jsonc(path).compilerOptions as Record; expect(REQUIRED_ON.filter((flag) => opts[flag] !== true)).toEqual([]); }); it("lists every weakening compiler flag", () => { const found: [string, string][] = []; for (const path of PROJECTS) { const opts = jsonc(path).compilerOptions as Record; if (opts.skipLibCheck === true) found.push(["all three projects", "skipLibCheck"]); } expect(missingFrom(found)).toEqual([]); }); // --- Ruff ----------------------------------------------------------------------------------------- it("lists every ruff ignore and immutable call", () => { const toml = read("backend/ruff.toml"); const list = (key: string) => { const m = new RegExp(`^${key}\\s*=\\s*\\[([^\\]]*)\\]`, "m").exec(toml); return m ? [...m[1]!.matchAll(/"([^"]+)"/g)].map((x) => x[1]!) : []; }; const found: [string, string][] = [ ...list("ignore").map((code): [string, string] => ["backend + scripts", code]), ...list("extend-immutable-calls").map((c): [string, string] => ["backend + scripts", c]), ]; expect(toml).not.toMatch(/per-file-ignores/); // none today — a new one needs a row and this line expect(missingFrom(found)).toEqual([]); }); // --- basedpyright --------------------------------------------------------------------------------- it("lists the type-check mode, the baseline and every rule override", () => { const cfg = jsonc("backend/pyrightconfig.json"); const found: [string, string][] = []; if (cfg.typeCheckingMode !== "all" && cfg.typeCheckingMode !== "strict") found.push(["backend/app + alembic", "typeCheckingMode"]); found.push(["backend/app + alembic", "baseline.json"]); for (const [key, value] of Object.entries(cfg)) if (key.startsWith("report") && (value === false || value === "none" || value === "warning")) found.push(["backend/app + alembic", key]); expect(cfg.include).toEqual(["app", "alembic"]); // tests/ out — its row says why expect(missingFrom(found)).toEqual([]); }); // --- pytest --------------------------------------------------------------------------------------- it("keeps pytest strict: no deselection, no warning filters, --strict-markers", () => { const ini = read("backend/pytest.ini"); expect(ini).toMatch(/--strict-markers/); expect(ini).not.toMatch(/--deselect|--ignore|-p no:|filterwarnings|\s-k\s/); }); // --- knip ----------------------------------------------------------------------------------------- it("lists every knip production exclusion and ignore", () => { const cfg = jsonc("frontend/knip.jsonc") as { project?: string[] } & Record; const found: [string, string][] = (cfg.project ?? []) .filter((p) => p.startsWith("!")) .map((p): [string, string] => ["--production", p]); for (const key of Object.keys(cfg).filter((k) => k.startsWith("ignore"))) found.push(["knip", key]); expect(missingFrom(found)).toEqual([]); }); // --- accepted findings ---------------------------------------------------------------------------- it("accepts a CVE only with a statement, an expiry, and a row", () => { const yaml = read(".trivyignore.yaml"); const entries = yaml .split(/\n\s*- id:\s*/) .slice(1) .map((chunk) => ({ id: chunk.split("\n")[0]!.trim(), statement: /\n\s*statement:\s*\S/.test(chunk), expires: /\n\s*expired_at:\s*\d{4}-\d{2}-\d{2}/.test(chunk), })); expect(entries.filter((e) => !e.statement || !e.expires).map((e) => e.id)).toEqual([]); const pipIgnores = read(".gitea/workflows/check.yml") .split("\n") .filter((l) => !l.trim().startsWith("#")) // the step's comment explains the flag; it is not one .flatMap((l) => [...l.matchAll(/--ignore-vuln[ =](\S+)/g)].map((m) => m[1]!)); const found = [...entries.map((e) => e.id), ...pipIgnores].map((id): [string, string] => [ "accepted", id, ]); expect(missingFrom(found)).toEqual([]); }); });