Unclaimed files left in the canonical checkout. Inventory: canonical-cleanup-inventory.md (commit-as-evidence groups). detect-secrets scan clean on 2026-11-02: hits are sha256 pins, git object ids, test sentinels already in tracked tests and unit-file lines.
58 lines
3.4 KiB
JavaScript
58 lines
3.4 KiB
JavaScript
// Row 170 (#1667) mutants, the brief's two: each an exact-once text edit in one candidate file,
|
|
// each named with the test files to run and the test titles that must fail on it. Driven by
|
|
// mutants.sh, run from the worktree.
|
|
// node mutants.mjs list | check | apply NAME | expect NAME LOG
|
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
|
|
const B = 'packages/bus/src/broker.mjs', BT = 'packages/bus/tests/tester.test.mjs';
|
|
const C = 'packages/cli/src/cli.mjs', CT = 'packages/cli/tests/tester.test.mjs';
|
|
const ITEM1 = 'a tester message to a role no run holds refuses tester-target-not-running and stores nothing';
|
|
const ITEM2 = 'a tester message is refused while the run is only launched, and routes once it claims';
|
|
const ITEM4 = 'talk --tester to a stopped role exits 3, prints no sent line and stores nothing';
|
|
|
|
const M = [
|
|
['M1-bus-no-tester-check', B, [BT, CT], [ITEM1, ITEM2, ITEM4],
|
|
" if (s.tester && !this.#recipient(s.business, to, thread)) fail('tester-target-not-running');\n", ""],
|
|
['M2-cli-no-tester-branch', C, [CT], [ITEM4],
|
|
" if (e.code === \"tester-target-not-running\" && parsed.flags.has(\"--tester\")) {\n throw new CliError(`${to} isn't running, so the tester's message was refused and nothing was stored`, 3);\n }\n", ""],
|
|
];
|
|
|
|
const [verb, name, log] = process.argv.slice(2);
|
|
if (verb === 'list') {
|
|
const seen = new Set();
|
|
for (const [n, file, tests] of M) {
|
|
if (seen.has(n)) { console.error(`duplicate ${n}`); process.exit(2); }
|
|
seen.add(n);
|
|
console.log(`${n}\t${file}\t${tests.join(' ')}`);
|
|
}
|
|
} else if (verb === 'check') {
|
|
// Read only: every mutant's old text must match exactly once, every named test file must exist
|
|
// and every named title must be one of its test files' titles.
|
|
let bad = 0;
|
|
for (const [n, file, tests, titles, before] of M) {
|
|
const count = readFileSync(file, 'utf8').split(before).length - 1;
|
|
if (count !== 1) { bad++; console.log(`${n}\t${file}\tmatches ${count}`); }
|
|
let text = '';
|
|
for (const t of tests) try { text += readFileSync(t, 'utf8'); } catch { bad++; console.log(`${n}\tmissing test ${t}`); }
|
|
for (const title of titles) if (!text.includes(JSON.stringify(title)) && !text.includes(`'${title}'`)) { bad++; console.log(`${n}\tno test titled ${title}`); }
|
|
}
|
|
console.log(`${M.length} mutants, ${bad} problems`);
|
|
process.exit(bad ? 1 : 0);
|
|
} else if (verb === 'apply') {
|
|
const m = M.find(x => x[0] === name);
|
|
if (!m) { console.error(`no mutant ${name}`); process.exit(2); }
|
|
const [, file, , , before, after] = m;
|
|
const text = readFileSync(file, 'utf8'), n = text.split(before).length - 1;
|
|
if (n !== 1) { console.error(`${name}: expected one match in ${file}, found ${n}`); process.exit(3); }
|
|
writeFileSync(file, text.replace(before, () => after));
|
|
} else if (verb === 'expect') {
|
|
// The spec reporter marks a failed test "✖ <title> (<ms>)". Every named title must be among them.
|
|
const m = M.find(x => x[0] === name);
|
|
if (!m) { console.error(`no mutant ${name}`); process.exit(2); }
|
|
const failed = new Set([...readFileSync(log, 'utf8').matchAll(/^\s*✖ (.*) \([\d.]+ms\)$/gm)].map(x => x[1]));
|
|
const missing = m[3].filter(t => !failed.has(t));
|
|
for (const t of failed) console.log(`${name}\tfailed\t${t}`);
|
|
for (const t of missing) console.log(`${name}\tDID NOT FAIL\t${t}`);
|
|
process.exit(missing.length ? 1 : 0);
|
|
} else { console.error('usage: mutants.mjs list | check | apply NAME | expect NAME LOG'); process.exit(2); }
|