Files
jason.woltje 6bad02612a lead: commit dewey's untracked evidence (721 files: queue-124-review queue-143 queue-149 queue-165-review queue-170 queue-175 queue-177 queue-184 queue-191)
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.
2026-11-02 15:05:14 -06:00

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); }