Files
brain/tools/plan-lock-judge-mode.test.mjs
T

51 lines
2.9 KiB
JavaScript

// tools/plan-lock-judge-mode.test.mjs
// Seal-time judge_mode validation (defense-in-depth).
// Источник: spec 2026-06-09-sealed-plan-production-design.md §11 + 2026-06-09-gate1-and-se2-design.md §4 (SE-a).
import { describe, it, expect } from 'vitest';
import { freezePlan, freezeArtifact, assertValidJudgeMode } from './plan-lock.mjs';
const KEY = 'plan-lock-test-key';
const STEPS = [{ n: 1, op: 'Write', object: 'tools/foo.mjs', intent: 'x' }];
const ART = { sections: { '§1': 'a' }, goal: 'g' };
describe('assertValidJudgeMode (seal-time guard)', () => {
it('accepts null / undefined / shadow / live-block', () => {
expect(() => assertValidJudgeMode(null)).not.toThrow();
expect(() => assertValidJudgeMode(undefined)).not.toThrow();
expect(() => assertValidJudgeMode('shadow')).not.toThrow();
expect(() => assertValidJudgeMode('live-block')).not.toThrow();
});
it('throws (fail-CLOSE) on bogus values: typo / wrong case / wrong type / inert / empty', () => {
expect(() => assertValidJudgeMode('Shadow')).toThrow();
expect(() => assertValidJudgeMode('live_block')).toThrow();
expect(() => assertValidJudgeMode('inert')).toThrow();
expect(() => assertValidJudgeMode(42)).toThrow();
expect(() => assertValidJudgeMode({})).toThrow();
expect(() => assertValidJudgeMode('')).toThrow();
});
});
describe('freezePlan — judge_mode validated at seal', () => {
it('seals with valid judgeMode (shadow / live-block / null default)', () => {
expect(freezePlan({ steps: STEPS, judgeMode: 'live-block', key: KEY, nowMs: 1 }).judge_mode).toBe('live-block');
expect(freezePlan({ steps: STEPS, judgeMode: 'shadow', key: KEY, nowMs: 1 }).judge_mode).toBe('shadow');
expect(freezePlan({ steps: STEPS, key: KEY, nowMs: 1 }).judge_mode).toBe(null); // default — legacy ok
});
it('throws (fail-CLOSE) on bogus judgeMode — no seal produced', () => {
expect(() => freezePlan({ steps: STEPS, judgeMode: 'Shadow', key: KEY, nowMs: 1 })).toThrow();
expect(() => freezePlan({ steps: STEPS, judgeMode: 'live_block', key: KEY, nowMs: 1 })).toThrow();
});
});
describe('freezeArtifact — embedded judge_mode validated at seal', () => {
it('seals with valid embedded judge_mode; legacy artifact without the field still seals', () => {
expect(freezeArtifact({ artifact: { ...ART, judge_mode: 'live-block' }, key: KEY, nowMs: 1 }).judge_mode).toBe('live-block');
expect(freezeArtifact({ artifact: { ...ART, judge_mode: 'shadow' }, key: KEY, nowMs: 1 }).judge_mode).toBe('shadow');
expect(freezeArtifact({ artifact: ART, key: KEY, nowMs: 1 }).artifact_id).toMatch(/^[0-9a-f]{64}$/); // legacy, нет поля → ok
});
it('throws (fail-CLOSE) on bogus embedded judge_mode', () => {
expect(() => freezeArtifact({ artifact: { ...ART, judge_mode: 'Shadow' }, key: KEY, nowMs: 1 })).toThrow();
expect(() => freezeArtifact({ artifact: { ...ART, judge_mode: 99 }, key: KEY, nowMs: 1 })).toThrow();
});
});