Files
reverse-engineering-anything/tests/boundary/filesystem/staticCheckReports.test.ts
T
Ysopwnable d96550f2f6 ci: summarize static checks and retain complete reports (#2242)
Keep static feedback concise while retaining complete diagnostic reports,
visible lint errors, module-boundary checks, and the 3% duplication gate.
Upload complete reports after static failures and restore their outputs
from Turbo caches. Preserve unambiguous, checkout-relative clone locations.

Bind both lint caches to the website, documentation configuration and
experiment sources actually scanned by oxlint, including ignore policy.
Guard this ownership by comparing live checker discovery with resolved
Turbo inputs. A real website syntax error now invalidates a cached pass.

Validation: full current-head CI and the separate Go lane passed. Seven
real-checker regressions passed; failure-status and stale-report mutations
were rejected, and restored source passed. Actual Turbo restored both
reports byte-for-byte; full static/docs/workflow checks passed.

Closes #2238.
Refs #2190.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-13 01:02:35 +08:00

215 lines
6.8 KiB
TypeScript

import { spawnSync } from "node:child_process";
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
import { join, sep } from "node:path";
import { fileURLToPath } from "node:url";
import { expect, it } from "vitest";
import { z } from "zod";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
const script = fileURLToPath(
new URL("../../../scripts/ci/static-check-report.mjs", import.meta.url),
);
const repository = fileURLToPath(new URL("../../../", import.meta.url));
const fixture = async () => {
const root = await createTestTempDirectory("rea-static-report-");
await Promise.all([mkdir(join(root, "src")), mkdir(join(root, "tests"))]);
await writeFile(
join(root, ".oxlintrc.json"),
JSON.stringify({
rules: { "no-debugger": "error", "no-unused-vars": "warn" },
}),
);
await writeFile(
join(root, ".jscpd.json"),
JSON.stringify({ threshold: 3, minLines: 5, minTokens: 50 }),
);
return root;
};
const invoke = (root: string, mode: string, ...arguments_: string[]) =>
spawnSync(process.execPath, [script, mode, ...arguments_], {
cwd: root,
encoding: "utf8",
timeout: 15_000,
});
const lintDiagnostics = async (root: string) =>
z
.object({
diagnostics: z.array(
z.object({
severity: z.string(),
code: z.string().optional(),
filename: z.string(),
}),
),
})
.parse(
JSON.parse(
await readFile(join(root, ".cache/static-checks/oxlint.json"), "utf8"),
),
).diagnostics;
it("includes every actually linted file in both cached lint task inputs", () => {
const scan = spawnSync(
process.execPath,
[join(repository, "node_modules/oxlint/bin/oxlint"), "--debug=files"],
{ cwd: repository, encoding: "utf8", timeout: 15_000 },
);
expect(scan.error).toBeUndefined();
expect(scan.status, scan.stderr).toBe(0);
const files = scan.stdout
.trim()
.split(/\r?\n/u)
.map((path) => path.split(sep).join("/"));
expect(files.length).toBeGreaterThan(0);
const dry = spawnSync(
process.execPath,
[
join(repository, "node_modules/turbo/bin/turbo"),
"run",
"lint",
"lint:built",
"--dry=json",
],
{
cwd: repository,
encoding: "utf8",
timeout: 15_000,
maxBuffer: 16 * 1024 * 1024,
},
);
expect(dry.error).toBeUndefined();
expect(dry.status, dry.stderr).toBe(0);
const { tasks } = z
.object({
tasks: z.array(
z.object({
task: z.string(),
inputs: z.record(z.string(), z.string()),
}),
),
})
.parse(JSON.parse(dry.stdout));
const lintTasks = tasks.filter(({ task }) =>
["lint", "lint:built"].includes(task),
);
expect(lintTasks).toHaveLength(2);
for (const task of lintTasks)
expect(
files.filter((file) => !Object.hasOwn(task.inputs, file)),
`${task.task} omits actual checker inputs`,
).toEqual([]);
});
it("summarizes warnings without losing their complete diagnostic records", async () => {
const root = await fixture();
await writeFile(join(root, "src/warning.js"), "const forgotten = 1;\n");
const result = invoke(root, "lint");
expect(result.error).toBeUndefined();
expect(result.status).toBe(0);
expect(result.stdout).toContain("0 errors, 1 warnings");
expect(result.stdout.split("\n").length).toBeLessThan(10);
expect(await lintDiagnostics(root)).toEqual([
expect.objectContaining({
severity: "warning",
code: "eslint(no-unused-vars)",
filename: "src/warning.js",
}),
]);
});
it("keeps lint errors failing and visible alongside the retained warnings", async () => {
const root = await fixture();
await writeFile(
join(root, "src/fault.js"),
"const forgotten = 1;\ndebugger;\n",
);
const result = invoke(root, "lint");
expect(result.error).toBeUndefined();
expect(result.status).toBe(1);
expect(result.stdout).toContain("1 errors, 1 warnings");
expect(result.stderr).toContain("src/fault.js:2:1");
expect(result.stderr).toContain("eslint(no-debugger)");
expect(await lintDiagnostics(root)).toEqual(
expect.arrayContaining([
expect.objectContaining({ severity: "warning" }),
expect.objectContaining({ severity: "error" }),
]),
);
});
it("keeps malformed source failing with its parser diagnostic", async () => {
const root = await fixture();
await writeFile(join(root, "src/malformed.js"), "const value = ;\n");
const result = invoke(root, "lint");
expect(result.error).toBeUndefined();
expect(result.status).not.toBe(0);
expect(result.stderr).toContain("src/malformed.js:1:");
expect(await lintDiagnostics(root)).toContainEqual(
expect.objectContaining({ severity: "error" }),
);
});
it("preserves the duplication threshold and complete clone locations", async () => {
const root = await fixture();
const source = [
"export function calculate(value) {",
...Array.from({ length: 34 }, (_, index) => ` value += ${index + 1};`),
" return value;",
"}",
].join("\n");
await Promise.all([
writeFile(join(root, "src/same.ts"), source),
writeFile(join(root, "tests/same.ts"), source),
]);
const result = invoke(root, "duplicates");
expect(result.error).toBeUndefined();
expect(result.status).toBe(1);
expect(result.stdout).toContain("Duplication:");
expect(result.stdout.split("\n").length).toBeLessThan(15);
const report = z
.object({
statistics: z.object({ total: z.object({ percentage: z.number() }) }),
duplicates: z.array(
z.object({
firstFile: z.object({ name: z.string() }),
secondFile: z.object({ name: z.string() }),
}),
),
})
.parse(
JSON.parse(
await readFile(
join(root, ".cache/static-checks/jscpd/jscpd-report.json"),
"utf8",
),
),
);
expect(report.statistics.total.percentage).toBeGreaterThan(3);
expect(report.duplicates).toHaveLength(1);
expect(
[
report.duplicates[0]?.firstFile.name,
report.duplicates[0]?.secondFile.name,
].sort(),
).toEqual(["src/same.ts", "tests/same.ts"]);
});
it.each([
["lint", "oxlint.json"],
["duplicates", "jscpd/jscpd-report.json"],
])(
"removes stale %s evidence when the checker cannot run",
async (mode, name) => {
const root = await fixture();
const reportPath = join(root, ".cache/static-checks", name);
await mkdir(join(root, ".cache/static-checks/jscpd"), { recursive: true });
await writeFile(reportPath, '{"previous_success":true}');
const result = invoke(root, mode, "--rea-invalid-checker-option");
expect(result.error).toBeUndefined();
expect(result.status).not.toBe(0);
expect(result.stderr).toContain("rea-invalid-checker-option");
await expect(access(reportPath)).rejects.toMatchObject({ code: "ENOENT" });
},
);