a5d30f38a3
Этап 2c эпика роутер-реестр: оживление машины охвата как замена цепочкам L.
- registry-initial-inputs.mjs: токены-данности (category:given) для initialInputs.
- registry-graph-health.test.mjs: граф ацикличен, рёбра producer-consumer.
- coverage-wiring.mjs: мост recommended skills -> readinessChecklist -> {cards, ready, holes}; ready=нет-дыр.
- enforce-judge-gate.mjs: coverageCardsFor/coverageGate — карточки + стоп при дыре (инъекция-выкл).
- замок словаря (vocabTokens) на живом пути; гайд по стене: автономность + уроки сессии.
Регрессия: 4375 passed (канонический свод владельца).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
48 lines
2.3 KiB
JavaScript
48 lines
2.3 KiB
JavaScript
import { describe, it, expect } from 'vitest';
|
||
import { coverageCardsFor, coverageGate } from './enforce-judge-gate.mjs';
|
||
|
||
// D4: чистые функции врезки охвата в гейт судьи. Мост (buildCoverageInput) инъектируется —
|
||
// тест герметичен (без I/O). Живой путь main() инъектирует реальный buildCoverageInput.
|
||
|
||
describe('enforce-judge-gate — врезка охвата (D4)', () => {
|
||
it('coverageCardsFor парсит skills-json и зовёт мост → cards/ready/holes', () => {
|
||
const content = '```skills-json\n["a","b"]\n```';
|
||
let seen = null;
|
||
const impl = ({ recommendedSkills }) => {
|
||
seen = recommendedSkills;
|
||
return { cards: [{ skill: 'a', needs: [], produces: ['x'] }], ready: true, holes: [] };
|
||
};
|
||
const out = coverageCardsFor({ content, coverageImpl: impl });
|
||
expect(seen).toEqual(['a', 'b']);
|
||
expect(out.cards).toEqual([{ skill: 'a', needs: [], produces: ['x'] }]);
|
||
expect(out.ready).toBe(true);
|
||
expect(out.holes).toEqual([]);
|
||
});
|
||
|
||
it('нет skills-json → cards:[], ready:true; мост НЕ зовётся (backward-compat)', () => {
|
||
const out = coverageCardsFor({ content: 'тело без навыков', coverageImpl: () => { throw new Error('не должен вызываться'); } });
|
||
expect(out.cards).toEqual([]);
|
||
expect(out.ready).toBe(true);
|
||
});
|
||
|
||
it('сбой моста → degraded:true (деградация не кирпичит)', () => {
|
||
const out = coverageCardsFor({ content: '```skills-json\n["a"]\n```', coverageImpl: () => { throw new Error('boom'); } });
|
||
expect(out.degraded).toBe(true);
|
||
expect(out.cards).toEqual([]);
|
||
});
|
||
|
||
it('coverageGate: дыра (ready:false) → block с перечнем дыр', () => {
|
||
const g = coverageGate({ ready: false, holes: [{ need: 'dast-report', neededBy: 'b' }] });
|
||
expect(g.block).toBe(true);
|
||
expect(g.reason).toMatch(/dast-report/);
|
||
});
|
||
|
||
it('coverageGate: нет дыр (ready:true) → не block', () => {
|
||
expect(coverageGate({ ready: true, holes: [] }).block).toBe(false);
|
||
});
|
||
|
||
it('coverageGate: degraded → не block (сбой охвата печать не стопорит)', () => {
|
||
expect(coverageGate({ ready: false, holes: [], degraded: true }).block).toBe(false);
|
||
});
|
||
});
|