Files
brain/tools/coverage-wiring.test.mjs
T
Дмитрий a5d30f38a3 feat(registry): живой охват 2c — данности, граф, coverage-wiring, врезка в гейт
Этап 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>
2026-06-19 15:18:22 +03:00

63 lines
3.0 KiB
JavaScript

import { describe, it, expect } from 'vitest';
import { readFileSync } from 'node:fs';
import { buildCoverageInput } from './coverage-wiring.mjs';
// Синтетический fs: реальный словарь + поддельные контракты в памяти.
function fakeFsWith(contracts) {
const files = Object.keys(contracts);
return {
readdirSync: () => files,
readFileSync: (p) => {
const s = String(p);
if (s.endsWith('capability-vocabulary.json')) return readFileSync('docs/registry/capability-vocabulary.json', 'utf8');
const name = files.find((f) => s.endsWith(f));
return JSON.stringify(contracts[name]);
},
};
}
const C = (skill, needs, produces) => ({
skill, kind: 'own', needs, produces,
constraints: [], 'preview-form': 'none', defaults: [], 'key-decisions': [], 'acceptance-criteria': [],
});
describe('coverage-wiring — мост охвата (D3)', () => {
it('полная цепочка (данность→producer→consumer) → ready:true, без дыр', () => {
const fs = fakeFsWith({
'a.contract.json': C('a', ['running-portal'], ['dast-report']),
'b.contract.json': C('b', ['dast-report'], ['go-live-verdict']),
});
const out = buildCoverageInput({ recommendedSkills: ['a', 'b'], fsImpl: fs });
expect(out.ready).toBe(true);
expect(out.holes).toEqual([]);
expect(out.cards.map((c) => c.skill).sort()).toEqual(['a', 'b']);
});
it('набор с непокрытой нуждой → ready:false, дыра видна', () => {
const fs = fakeFsWith({ 'b.contract.json': C('b', ['dast-report'], ['go-live-verdict']) });
const out = buildCoverageInput({ recommendedSkills: ['b'], fsImpl: fs });
expect(out.ready).toBe(false);
expect(out.holes.length).toBeGreaterThan(0);
expect(out.holes.some((h) => h.need === 'dast-report')).toBe(true);
});
it('cards несут {skill, needs, produces}', () => {
const fs = fakeFsWith({ 'a.contract.json': C('a', ['running-portal'], ['dast-report']) });
const out = buildCoverageInput({ recommendedSkills: ['a'], fsImpl: fs });
expect(out.cards).toEqual([{ skill: 'a', needs: ['running-portal'], produces: ['dast-report'] }]);
});
it('неизвестный рекомендованный навык не попадает в cards и не роняет мост', () => {
const fs = fakeFsWith({ 'a.contract.json': C('a', ['running-portal'], ['dast-report']) });
const out = buildCoverageInput({ recommendedSkills: ['a', 'nonexistent'], fsImpl: fs });
expect(out.cards.map((c) => c.skill)).toEqual(['a']);
});
it('D5: замок словаря активен на живом пути — неизвестный токен в errors', () => {
const fs = fakeFsWith({ 'bad.contract.json': C('bad', ['ghost-token-xyz'], ['running-portal']) });
const out = buildCoverageInput({ recommendedSkills: ['bad'], fsImpl: fs });
const err = out.errors.find((e) => e.skill === 'bad');
expect(err).toBeTruthy();
expect(err.errors.join(' ')).toMatch(/ghost-token-xyz/);
});
});