#!/usr/bin/env node import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import { spawnSync } from 'node:child_process'; import { fileURLToPath } from 'node:url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const gateScript = path.join(__dirname, 'approved_plan_continuity_gate.mjs'); function createFixture(files = {}) { const root = mkdtempSync(path.join(os.tmpdir(), 'approved-plan-continuity-')); for (const [relativePath, content] of Object.entries(files)) { const filePath = path.join(root, relativePath); mkdirSync(path.dirname(filePath), { recursive: true }); writeFileSync(filePath, typeof content === 'string' ? content : `${JSON.stringify(content, null, 2)}\n`); } return { root, path(...segments) { return path.join(root, ...segments); }, cleanup() { rmSync(root, { recursive: true, force: true }); }, }; } function runGate({ args = [], stdin = null } = {}) { const result = spawnSync(process.execPath, [gateScript, ...args], { input: stdin, encoding: 'utf8', }); let json = null; if (result.stdout && result.stdout.trim()) { try { json = JSON.parse(result.stdout); } catch { json = null; } } return { status: result.status, stdout: result.stdout, stderr: result.stderr, json, }; } const tests = [ { name: 'skeleton: gate script responds with placeholder envelope when given fixture input', run() { const fixture = createFixture({ 'input.json': { planId: 'plan-skeleton', currentTask: 'task-5', }, }); try { const result = runGate({ args: ['--compact', '--input', fixture.path('input.json')], }); if (result.status !== 0 && result.status !== null) { throw new Error(`expected controlled execution, got status=${result.status}\n${result.stderr || result.stdout}`); } if (!result.json || typeof result.json !== 'object') { throw new Error(`expected JSON output\nstdout=${result.stdout}`); } if (result.json.gate !== 'approved_plan_continuity') { throw new Error(`expected gate=approved_plan_continuity, got ${JSON.stringify(result.json.gate)}`); } } finally { fixture.cleanup(); } }, }, ]; const results = []; let failed = false; for (const test of tests) { try { test.run(); results.push({ test: test.name, ok: true }); } catch (error) { failed = true; results.push({ test: test.name, ok: false, error: error instanceof Error ? error.message : String(error), }); } } const summary = { total: tests.length, passed: results.filter((entry) => entry.ok).length, failed: results.filter((entry) => !entry.ok).length, }; process.stdout.write(`${JSON.stringify({ summary, results }, null, 2)}\n`); if (failed) process.exit(1);