// Stream H Task 3 — Workflow gate F2 unit tests (TDD). // References ./enforce-workflow-gate.mjs (the prod file under TDD). import { describe, it, expect } from 'vitest'; import { decide } from './enforce-workflow-gate.mjs'; describe('enforce-workflow-gate scriptPath approval (F2)', () => { it('blocks Workflow with new scriptPath without approval (RED phase, no prod file yet)', () => { const r = decide({ toolInput: { scriptPath: 'workflows/new-untested.mjs' }, approvedWorkflowScripts: [], scriptContent: 'export const meta = {name:"x",description:"y"}\nphase("X")', now: Date.now(), }); expect(r.block).toBe(true); expect(r.reason).toMatch(/F2.*approve_workflow_script/i); }); it('allows Workflow with approved scriptPath within 5min window', () => { const now = Date.now(); const r = decide({ toolInput: { scriptPath: 'workflows/x.mjs' }, approvedWorkflowScripts: [{ scriptPath: 'workflows/x.mjs', sha256: 'a'.repeat(64), ts: now }], scriptContent: 'export const meta={name:"x",description:"y"}', scriptSha256: 'a'.repeat(64), now, }); expect(r.block).toBe(false); }); it('blocks Workflow with resumeFromRunId param (F2 hardening)', () => { const r = decide({ toolInput: { scriptPath: 'workflows/x.mjs', resumeFromRunId: 'wf_abc123' }, approvedWorkflowScripts: [{ scriptPath: 'workflows/x.mjs', sha256: 'a'.repeat(64), ts: Date.now() }], scriptContent: 'x', scriptSha256: 'a'.repeat(64), now: Date.now(), }); expect(r.block).toBe(true); expect(r.reason).toMatch(/resumeFromRunId/); }); it('blocks Workflow whose scriptContent has dangerous pattern', () => { const r = decide({ toolInput: { scriptPath: 'workflows/x.mjs' }, approvedWorkflowScripts: [{ scriptPath: 'workflows/x.mjs', sha256: 'a'.repeat(64), ts: Date.now() }], scriptContent: 'process.env.ROUTER_LLM_KEY', scriptSha256: 'a'.repeat(64), now: Date.now(), }); expect(r.block).toBe(true); expect(r.reason).toMatch(/dangerous pattern.*ROUTER_LLM_KEY/i); }); it('blocks Workflow with sha256 mismatch (content changed since approval)', () => { const r = decide({ toolInput: { scriptPath: 'workflows/x.mjs' }, approvedWorkflowScripts: [{ scriptPath: 'workflows/x.mjs', sha256: 'a'.repeat(64), ts: Date.now() }], scriptContent: 'modified', scriptSha256: 'b'.repeat(64), now: Date.now(), }); expect(r.block).toBe(true); expect(r.reason).toMatch(/sha256.*mismatch/i); }); });