// tools/criterion-green.test.mjs // 5.6 (Машина 5 Пакет 5, Блок 3): по-критерийный производитель GREEN. Эмитит подписанный // подписантом green ТОЛЬКО при настоящем зелёном прогоне — мутация P18 как условие (сломал // изменённый код → тест обязан покраснеть; выжил → не проверяет → не green). code_fingerprint // (Δ2) детерминирован над содержимым изменённых файлов + тестов. Интеграция с критерий-гейтом // из judge-orchestrator (5.5): произведённый green проходит все 4 шага на свежем отпечатке. import { describe, it, expect } from 'vitest'; import { codeFingerprint, produceGreen } from './criterion-green.mjs'; import { verifyGreen } from './floor-signer.mjs'; import { runCriterionGate } from './judge-orchestrator.mjs'; const SK = 'producer-signer-key'; describe('codeFingerprint (5.6, Δ2): детерминированный отпечаток изменённых файлов', () => { it('детерминирован над одним содержимым, порядок ключей не важен', () => { const a = codeFingerprint({ 'a.mjs': 'x', 'a.test.mjs': 'y' }); const b = codeFingerprint({ 'a.test.mjs': 'y', 'a.mjs': 'x' }); expect(a).toBe(b); expect(a).toMatch(/^[0-9a-f]{64}$/); }); it('правка содержимого файла → другой отпечаток (green аннулируется)', () => { expect(codeFingerprint({ 'a.mjs': 'x' })).not.toBe(codeFingerprint({ 'a.mjs': 'x2' })); }); }); describe('produceGreen (5.6): подписанный по-критерийный GREEN; мутация P18 — условие настоящего green', () => { it('тест прошёл И мутация убита И ключ → green:true + валидная подпись подписанта', () => { const fp = codeFingerprint({ 'a.mjs': 'code', 'a.test.mjs': 'test' }); const rec = produceGreen({ criterion_id: 'c1', occurrence: 1, code_fingerprint: fp, coverage_of_changed: 1, testPassed: true, mutationKilled: true, signerKey: SK }); expect(rec.green).toBe(true); expect(rec.code_fingerprint).toBe(fp); expect(rec.coverage_of_changed).toBe(1); expect(verifyGreen({ criterion_id: rec.criterion_id, code_fingerprint: rec.code_fingerprint, occurrence: rec.occurrence, sig: rec.sig }, SK)).toBe(true); }); it('P18: мутация ВЫЖИЛА (тест не проверяет) → green:false, нет подписи', () => { const rec = produceGreen({ criterion_id: 'c1', occurrence: 1, code_fingerprint: 'fp', testPassed: true, mutationKilled: false, signerKey: SK }); expect(rec.green).toBe(false); expect(rec.reason).toBe('mutation-survived'); expect(rec.sig).toBeUndefined(); }); it('тест не прошёл → green:false', () => { const rec = produceGreen({ criterion_id: 'c1', occurrence: 1, code_fingerprint: 'fp', testPassed: false, mutationKilled: true, signerKey: SK }); expect(rec.green).toBe(false); expect(rec.reason).toBe('test-not-passed'); }); it('настоящий green, но нет ключа подписанта → green:false (fail-CLOSE)', () => { const rec = produceGreen({ criterion_id: 'c1', occurrence: 1, code_fingerprint: 'fp', testPassed: true, mutationKilled: true, signerKey: undefined }); expect(rec.green).toBe(false); expect(rec.reason).toBe('no-signer-key'); }); it('произведённый green проходит весь критерий-гейт (5.5) на свежем отпечатке', () => { const fp = codeFingerprint({ 'a.mjs': 'code' }); const rec = produceGreen({ criterion_id: 'c1', occurrence: 1, code_fingerprint: fp, testPassed: true, mutationKilled: true, signerKey: SK }); const gate = runCriterionGate({ criteria: [{ id: 'c1' }], greenRuns: [rec], sealedCriterionIds: ['c1'], currentFingerprints: { c1: fp }, signerKey: SK, }); expect(gate.passed).toBe(true); }); });