// Every fixture case runs through the real CLI in a fresh subprocess; the observed // exit/result/reason/rule/proposalRule/byteOffset must equal index.json. The owner // demo goldens are compared byte-for-byte. import { test } from "node:test"; import assert from "node:assert/strict"; import { spawnSync } from "node:child_process"; import { readFileSync, readdirSync, existsSync } from "node:fs"; import { join, dirname } from "node:path"; import { fileURLToPath } from "node:url"; import { renderText, run } from "../foundation-inspect.mjs"; import { exitFor } from "./resolve.mjs"; const HERE = dirname(fileURLToPath(import.meta.url)); const FIXTURES = join(HERE, "fixtures"); const CLI = join(HERE, "..", "foundation-inspect.mjs"); const INDEX = JSON.parse(readFileSync(join(FIXTURES, "index.json"), "utf8")); const ENV = { PATH: "/nonexistent", HOME: "/nonexistent", LANG: "C" }; const RAW_RISKY_RE = /[\u0000-\u0008\u000b-\u001f\u007f-\u009f\u2028\u2029\ufeff\u200b]/; function spawnJson(file) { const r = spawnSync(process.execPath, [CLI, "--json", file], { cwd: FIXTURES, env: ENV, encoding: "utf8", timeout: 30000 }); assert.equal(r.error, undefined); assert.equal(r.stderr, "", `${file}: stderr must be empty`); const parsed = JSON.parse(r.stdout); assert.ok(!("exit" in parsed), `${file}: exit is not a serialized field`); assert.equal(r.status, exitFor(parsed), `${file}: exit code derived from the closed result`); return { parsed, stdout: r.stdout, status: r.status }; } test("index is complete and every listed file exists", () => { assert.equal(INDEX.count, INDEX.cases.length); assert.equal(INDEX.generator, "build-fixtures.mjs"); const names = INDEX.cases.map((c) => c.name); assert.deepEqual(names, [...names].sort(), "index sorted by name"); assert.equal(new Set(names).size, names.length); const listed = new Set(INDEX.cases.map((c) => c.file)); for (const dir of ["bundles", "raw"]) { for (const f of readdirSync(join(FIXTURES, dir))) assert.ok(listed.has(`${dir}/${f}`), `${dir}/${f} not in index`); } for (const c of INDEX.cases) assert.ok(existsSync(join(FIXTURES, c.file)), c.file); }); test("every fixture case matches its expected verdict through the CLI", () => { const failures = []; for (const c of INDEX.cases) { const { parsed, status } = spawnJson(c.file); const got = { exit: status, result: parsed.result, reason: parsed.reason, rule: parsed.rule, proposalRule: parsed.proposal === null ? null : parsed.proposal.rule, }; const want = { exit: c.expect.exit, result: c.expect.result, reason: c.expect.reason, rule: c.expect.rule, proposalRule: c.expect.proposalRule }; if ("byteOffset" in c.expect) { got.byteOffset = parsed.diagnostic === null ? null : parsed.diagnostic.byteOffset; want.byteOffset = c.expect.byteOffset; if (parsed.diagnostic !== null) assert.equal(parsed.diagnostic.inputPath, null, `${c.name}: path never echoed on parse failure`); } else { assert.equal(parsed.diagnostic, null, `${c.name}: diagnostic only on I/O or parse failure`); } if (JSON.stringify(got) !== JSON.stringify(want)) failures.push(`${c.name}: got ${JSON.stringify(got)} want ${JSON.stringify(want)}`); if (status === 0) assert.equal(parsed.rule, null, c.name); if (parsed.proposal !== null) assert.equal(parsed.result, parsed.proposal.result, c.name); } assert.deepEqual(failures, []); }); test("in-process text rendering agrees with the JSON result for every case", () => { for (const c of INDEX.cases) { const file = join(FIXTURES, c.file); const text = run([file]); const json = run(["--json", file]); assert.equal(text.json, false); assert.deepEqual(text.result, json.result, c.name); assert.equal(text.exit, c.expect.exit, c.name); assert.equal(json.exit, c.expect.exit, c.name); const rendered = renderText(text.result); assert.ok(rendered.startsWith("SYNTHETIC PREVIEW — NO LIVE EFFECTS\n"), c.name); assert.ok(/\ndiagnostic: [^\n]*\n$/.test(rendered), `${c.name}: text ends with the diagnostic line, no exit line`); assert.ok(!/^exit/m.test(rendered), c.name); assert.ok(!RAW_RISKY_RE.test(rendered), `${c.name}: raw control in text output`); } }); test("owner demo goldens match byte-for-byte", () => { const names = readdirSync(join(FIXTURES, "demo")).filter((f) => f.endsWith(".json") && !f.includes(".expected.")); assert.equal(names.length, 5); for (const name of names) { const base = name.slice(0, -5); const file = join(FIXTURES, "demo", name); const text = spawnSync(process.execPath, [CLI, file], { cwd: FIXTURES, env: ENV, encoding: "utf8" }); const json = spawnSync(process.execPath, [CLI, "--json", file], { cwd: FIXTURES, env: ENV, encoding: "utf8" }); assert.equal(text.stdout, readFileSync(join(FIXTURES, "demo", `${base}.expected.txt`), "utf8"), `${base}.expected.txt`); assert.equal(json.stdout, readFileSync(join(FIXTURES, "demo", `${base}.expected.json`), "utf8"), `${base}.expected.json`); assert.equal(text.status, json.status); assert.equal(String(text.status), readFileSync(join(FIXTURES, "demo", `${base}.expected.exit`), "utf8").trim()); // the demo copy equals the indexed bundle it was copied from assert.equal(readFileSync(file, "utf8"), readFileSync(join(FIXTURES, "bundles", name), "utf8"), `${base}: demo copy drifted`); } });