import { describe, it, expect } from 'vitest'; import { classifyMutationResult, runMutationForFile } from './mutate-runner.mjs'; import { mkdtempSync, writeFileSync, readFileSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; describe('classifyMutationResult (чистая классификация P18)', () => { it('baseline не зелёный → killed:false reason baseline-not-green', () => { expect(classifyMutationResult({ baselineGreen: false, mutantOutcomes: [] })) .toEqual({ mutationKilled: false, reason: 'baseline-not-green' }); }); it('нет валидных мутантов (все loadError) → no-valid-mutants', () => { expect(classifyMutationResult({ baselineGreen: true, mutantOutcomes: [{ allGreen: false, loadError: true }] })) .toEqual({ mutationKilled: false, reason: 'no-valid-mutants' }); }); it('хоть один валидный мутант покраснел (allGreen:false, не loadError) → killed', () => { expect(classifyMutationResult({ baselineGreen: true, mutantOutcomes: [ { allGreen: true, loadError: false }, { allGreen: false, loadError: false } ] })) .toEqual({ mutationKilled: true, reason: 'ok' }); }); it('все валидные мутанты выжили (тест ничего не проверяет) → mutation-survived', () => { expect(classifyMutationResult({ baselineGreen: true, mutantOutcomes: [{ allGreen: true, loadError: false }] })) .toEqual({ mutationKilled: false, reason: 'mutation-survived' }); }); }); describe('runMutationForFile (in-place + восстановление SE-LB-3)', () => { it('восстанавливает оригинал файла после прогона (даже если мутант валит тест)', () => { const dir = mkdtempSync(join(tmpdir(), 'mut-')); const file = join(dir, 'x.mjs'); const original = 'export const f = (a, b) => a === b;\n'; writeFileSync(file, original); // Фейковый runTest: baseline зелёный; на мутированном содержимом (=== заменён) — красный. const runTest = () => { const cur = readFileSync(file, 'utf-8'); const baseline = cur === original; return { allGreen: baseline, numPassed: baseline ? 1 : 0, numFailed: baseline ? 0 : 1, loadError: false }; }; const res = runMutationForFile({ filePath: file, generate: (s) => [{ label: 'm', mutated: s.replace('===', '!==') }], runTest }); expect(res.baselineGreen).toBe(true); expect(res.mutantOutcomes.some((o) => o.allGreen === false)).toBe(true); expect(readFileSync(file, 'utf-8')).toBe(original); // восстановлен rmSync(dir, { recursive: true, force: true }); }); it('baseline красный → мутацию не запускает, baselineGreen:false, файл цел', () => { const dir = mkdtempSync(join(tmpdir(), 'mut-')); const file = join(dir, 'y.mjs'); writeFileSync(file, 'broken'); const runTest = () => ({ allGreen: false, numPassed: 0, numFailed: 1, loadError: false }); const res = runMutationForFile({ filePath: file, generate: () => [{ label: 'm', mutated: 'mutant' }], runTest }); expect(res.baselineGreen).toBe(false); expect(res.mutantOutcomes).toEqual([]); expect(readFileSync(file, 'utf-8')).toBe('broken'); rmSync(dir, { recursive: true, force: true }); }); });