import { describe, it, expect } from 'vitest'; import { buildBlessedOps, loadBlessedOpsForSession } from './blessed-ops.mjs'; import { freezePlan } from './plan-lock.mjs'; describe('buildBlessedOps (D1 — белый список из опечатанного deploy-плана)', () => { const K = 'k-bless'; const norm = (p) => String(p); const mk = (over) => freezePlan({ steps: [{ op: 'Bash', object: 'composer install' }], kind: 'deploy', judgeMode: 'live-block', key: K, nowMs: 1, ...over }); it('грант на хеш + kind:deploy + live-block + valid seal → предикат пускает Bash-шаг дословно', () => { const plan = mk(); const bless = buildBlessedOps({ frozenPlan: plan, grants: [{ action: `ops-runbook:${plan.plan_id}`, ts: 1 }], key: K, verifyImpl: () => true, normalize: norm }); expect(typeof bless).toBe('function'); expect(bless('composer install')).toBe(true); expect(bless('rm -rf /')).toBe(false); }); it('нет гранта на этот хеш → null', () => { const plan = mk(); expect(buildBlessedOps({ frozenPlan: plan, grants: [{ action: 'ops-runbook:OTHER', ts: 1 }], key: K, verifyImpl: () => true, normalize: norm })).toBe(null); }); it('план не kind:deploy → null', () => { const plan = freezePlan({ steps: [{ op: 'Bash', object: 'composer install' }], judgeMode: 'live-block', key: K, nowMs: 1 }); expect(buildBlessedOps({ frozenPlan: plan, grants: [{ action: `ops-runbook:${plan.plan_id}`, ts: 1 }], key: K, verifyImpl: () => true, normalize: norm })).toBe(null); }); it('печать невалидна (verifyImpl→false) → null', () => { const plan = mk(); expect(buildBlessedOps({ frozenPlan: plan, grants: [{ action: `ops-runbook:${plan.plan_id}`, ts: 1 }], key: K, verifyImpl: () => false, normalize: norm })).toBe(null); }); it('judge_mode не live-block → null', () => { const plan = mk({ judgeMode: 'shadow' }); expect(buildBlessedOps({ frozenPlan: plan, grants: [{ action: `ops-runbook:${plan.plan_id}`, ts: 1 }], key: K, verifyImpl: () => true, normalize: norm })).toBe(null); }); it('нет frozenPlan → null', () => { expect(buildBlessedOps({ frozenPlan: null, grants: [{ action: 'ops-runbook:x', ts: 1 }], key: K, verifyImpl: () => true })).toBe(null); }); }); describe('loadBlessedOpsForSession (D1 — gated I/O: нет гранта → план не грузим)', () => { it('нет ops-runbook-грантов → null, план НЕ грузится (Δ9 сохранён)', () => { let planLoaded = false; const r = loadBlessedOpsForSession('S', { loadGrantsImpl: () => [], loadPlanImpl: () => { planLoaded = true; return null; }, keyImpl: () => 'k', }); expect(r).toBe(null); expect(planLoaded).toBe(false); }); it('есть грант на хеш + deploy-план → предикат построен', () => { const key = 'k-load'; const plan = freezePlan({ steps: [{ op: 'Bash', object: 'composer install' }], kind: 'deploy', judgeMode: 'live-block', key, nowMs: 1 }); const bless = loadBlessedOpsForSession('S', { loadGrantsImpl: () => [{ action: `ops-runbook:${plan.plan_id}`, ts: 1 }], loadPlanImpl: () => plan, keyImpl: () => key, }); expect(typeof bless).toBe('function'); expect(bless('composer install')).toBe(true); }); });