397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
46 lines
2.7 KiB
JavaScript
46 lines
2.7 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* mutate-runner (Level B, P18) — живая мутация одного файла под один тест.
|
|
* baseline-GREEN до мутации (SE-LB-2: краснота атрибутируема мутации, не пред-существующему провалу);
|
|
* мутация IN-PLACE с железобетонным восстановлением (SE-LB-3): тест импортирует РЕАЛЬНЫЙ путь, поэтому
|
|
* мутанта надо положить туда же; оригинал держим в памяти и пишем обратно в finally + сверяем восстановление.
|
|
* Утечка мутанта при жёстком kill ловится потребителем по несовпадению отпечатка (defense-in-depth).
|
|
* classifyMutationResult — чистая: убит ⇔ ≥1 валидный (не loadError) мутант покраснел.
|
|
*/
|
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
|
|
export function classifyMutationResult({ baselineGreen, mutantOutcomes = [] }) {
|
|
if (baselineGreen !== true) return { mutationKilled: false, reason: 'baseline-not-green' };
|
|
const valid = (mutantOutcomes || []).filter((o) => o && o.loadError !== true);
|
|
if (valid.length === 0) return { mutationKilled: false, reason: 'no-valid-mutants' };
|
|
const killed = valid.some((o) => o.allGreen === false);
|
|
return killed ? { mutationKilled: true, reason: 'ok' } : { mutationKilled: false, reason: 'mutation-survived' };
|
|
}
|
|
|
|
/**
|
|
* Прогнать мутацию для filePath под testFile. generate/runTest инъектируются (юнит-тесты — фейки;
|
|
* live main подаёт generateMutants и (tf)=>runVitestJson(tf, gitCwd)). testFile передаётся в runTest.
|
|
*/
|
|
export function runMutationForFile({ filePath, testFile, generate, runTest }) {
|
|
const original = readFileSync(filePath, 'utf-8');
|
|
const baseline = runTest(testFile);
|
|
if (!(baseline.allGreen === true && baseline.numPassed > 0)) {
|
|
return { baselineGreen: false, testCount: baseline.numPassed || 0, mutantOutcomes: [] };
|
|
}
|
|
const mutants = generate(original) || [];
|
|
const mutantOutcomes = [];
|
|
try {
|
|
for (const m of mutants) {
|
|
writeFileSync(filePath, m.mutated);
|
|
const r = runTest(testFile);
|
|
mutantOutcomes.push({ label: m.label, allGreen: r.allGreen, loadError: r.loadError });
|
|
}
|
|
} finally {
|
|
writeFileSync(filePath, original); // восстановление гарантировано
|
|
}
|
|
if (readFileSync(filePath, 'utf-8') !== original) {
|
|
throw new Error('mutate-runner: восстановление файла не удалось (fail-CLOSE)');
|
|
}
|
|
return { baselineGreen: true, testCount: baseline.numPassed, mutantOutcomes };
|
|
}
|