397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
86 lines
4.4 KiB
JavaScript
86 lines
4.4 KiB
JavaScript
// tools/door-coverage.test.mjs
|
|
import { describe, it, expect } from 'vitest';
|
|
import { auditDoors } from './door-coverage.mjs';
|
|
import { auditExempt } from './door-coverage.mjs';
|
|
|
|
describe('auditDoors (P15-b)', () => {
|
|
it('flags a tool not covered by the supreme matcher', () => {
|
|
const r = auditDoors({ tools: ['Write', 'Bash', 'WebFetch'], matcher: ['Write', 'Bash'], seeds: ['AskUserQuestion'] });
|
|
expect(r.uncovered).toEqual(['WebFetch']);
|
|
expect(r.ok).toBe(false);
|
|
});
|
|
it('star matcher covers everything → ok', () => {
|
|
const r = auditDoors({ tools: ['Write', 'Bash', 'WebFetch'], matcher: ['*'], seeds: [] });
|
|
expect(r.uncovered).toEqual([]);
|
|
expect(r.ok).toBe(true);
|
|
});
|
|
it('seeds are not counted as uncovered', () => {
|
|
const r = auditDoors({ tools: ['AskUserQuestion', 'Write'], matcher: ['Write'], seeds: ['AskUserQuestion'] });
|
|
expect(r.uncovered).toEqual([]);
|
|
expect(r.ok).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('auditExempt (страховка зелёного прохода, F)', () => {
|
|
// классификатор мутации инъектируется (в проде — общий с supreme-gate)
|
|
const SAFE = new Set(['Read', 'Grep', 'Glob', 'LS', 'AskUserQuestion', 'EnterPlanMode']);
|
|
const isMutating = (t) => !SAFE.has(t) && !String(t).startsWith('Skill:');
|
|
|
|
it('мутирующий инструмент в зелёном проходе → флаг', () => {
|
|
const r = auditExempt({ exempt: ['Read', 'Bash'], isMutating });
|
|
expect(r.ok).toBe(false);
|
|
expect(r.flagged).toEqual(['Bash']);
|
|
});
|
|
it('только-смотрящие + семена в зелёном проходе → ok', () => {
|
|
const r = auditExempt({ exempt: ['Read', 'Grep', 'AskUserQuestion'], isMutating });
|
|
expect(r.ok).toBe(true);
|
|
expect(r.flagged).toEqual([]);
|
|
});
|
|
});
|
|
|
|
// ── R-24 (Блок B Класс 2): door-coverage helpers + anti-drift seeds ──
|
|
import { CANONICAL_MUTATING_TOOLS, isMutatingTool, extractGateMatcher } from './door-coverage.mjs';
|
|
import { SEED_TOOLS as GATE_SEED_TOOLS } from './enforce-supreme-gate.mjs';
|
|
|
|
describe('CANONICAL_MUTATING_TOOLS / isMutatingTool (R-24)', () => {
|
|
it('канонический список включает ключевые мутирующие инструменты', () => {
|
|
for (const t of ['Edit', 'Write', 'MultiEdit', 'NotebookEdit', 'Bash', 'Task', 'Skill']) {
|
|
expect(CANONICAL_MUTATING_TOOLS).toContain(t);
|
|
}
|
|
});
|
|
it('isMutatingTool: мутирующий → true, observe-only → false', () => {
|
|
expect(isMutatingTool('Write')).toBe(true);
|
|
expect(isMutatingTool('Read')).toBe(false);
|
|
expect(isMutatingTool('EnterPlanMode')).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('extractGateMatcher (R-24)', () => {
|
|
it('хук не найден → []', () => {
|
|
expect(extractGateMatcher({}, 'enforce-supreme-gate.mjs')).toEqual([]);
|
|
expect(extractGateMatcher({ hooks: { PreToolUse: [] } }, 'enforce-supreme-gate.mjs')).toEqual([]);
|
|
});
|
|
it('matcher "*" → ["*"]', () => {
|
|
const settings = { hooks: { PreToolUse: [{ matcher: '*', hooks: [{ type: 'command', command: 'node tools/enforce-supreme-gate.mjs' }] }] } };
|
|
expect(extractGateMatcher(settings, 'enforce-supreme-gate.mjs')).toEqual(['*']);
|
|
});
|
|
it('matcher "Edit|Write|Bash" → ["Edit","Write","Bash"]', () => {
|
|
const settings = { hooks: { PreToolUse: [{ matcher: 'Edit|Write|Bash', hooks: [{ command: 'node tools/enforce-supreme-gate.mjs' }] }] } };
|
|
expect(extractGateMatcher(settings, 'enforce-supreme-gate.mjs')).toEqual(['Edit', 'Write', 'Bash']);
|
|
});
|
|
it('пустой matcher "" → [] (аудит требует явного *)', () => {
|
|
const settings = { hooks: { PreToolUse: [{ matcher: '', hooks: [{ command: 'node tools/enforce-supreme-gate.mjs' }] }] } };
|
|
expect(extractGateMatcher(settings, 'enforce-supreme-gate.mjs')).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('SEED_TOOLS export (R-24 anti-drift, инвариантность)', () => {
|
|
it('экспортирован и содержит seed-инструменты стены', () => {
|
|
expect(GATE_SEED_TOOLS.has('EnterPlanMode')).toBe(true);
|
|
expect(GATE_SEED_TOOLS.has('AskUserQuestion')).toBe(true);
|
|
});
|
|
it('seed-инструменты НЕ числятся мутирующими (иначе auditExempt их флагует)', () => {
|
|
for (const t of GATE_SEED_TOOLS) expect(isMutatingTool(t)).toBe(false);
|
|
});
|
|
});
|