import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { execFileSync } from 'node:child_process'; import { writeFileSync, unlinkSync } from 'node:fs'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; import { resolvePhaseSignal, resolvePhaseSignalFromFile } from '../../lib/profiles/phase-signal-resolver.mjs'; const FULL_SIGNALS_FM = { phase_signals: [ { phase: 'research', effort: 'low', model: 'sonnet' }, { phase: 'plan', effort: 'standard' }, { phase: 'execute', effort: 'high', model: 'opus' }, { phase: 'review', effort: 'standard', model: 'sonnet' }, ], }; test('resolvePhaseSignal — returns {effort, model} for all 4 phases on full-signals brief', () => { for (const phase of ['research', 'plan', 'execute', 'review']) { const r = resolvePhaseSignal(FULL_SIGNALS_FM, phase); assert.ok(r && typeof r === 'object', `phase=${phase} should resolve non-null`); assert.ok(typeof r.effort === 'string', `phase=${phase} should have effort`); } }); test('resolvePhaseSignal — returns null when brief has no phase_signals', () => { const r = resolvePhaseSignal({ task: 'x' }, 'plan'); assert.equal(r, null); }); test('resolvePhaseSignal — returns partial {effort} with model undefined when signal omits model', () => { const r = resolvePhaseSignal(FULL_SIGNALS_FM, 'plan'); assert.equal(r.effort, 'standard'); assert.equal(r.model, undefined); assert.ok(!('model' in r), 'model key should be absent when not in signal'); }); test('resolvePhaseSignal — returns null when phase is not in PHASE_SIGNAL_PHASES', () => { assert.equal(resolvePhaseSignal(FULL_SIGNALS_FM, 'brief'), null); assert.equal(resolvePhaseSignal(FULL_SIGNALS_FM, 'continue'), null); assert.equal(resolvePhaseSignal(FULL_SIGNALS_FM, 'nonsense'), null); }); test('resolvePhaseSignal — defensive: null/non-object input returns null', () => { assert.equal(resolvePhaseSignal(null, 'plan'), null); assert.equal(resolvePhaseSignal(undefined, 'plan'), null); assert.equal(resolvePhaseSignal('string', 'plan'), null); assert.equal(resolvePhaseSignal({ phase_signals: 'not-array' }, 'plan'), null); }); test('resolvePhaseSignalFromFile + CLI shim — writes JSON to stdout, exit 0', () => { const fixture = join(tmpdir(), `phase-signal-test-${process.pid}.md`); writeFileSync(fixture, `--- type: trekbrief brief_version: "2.1" phase_signals: - phase: plan effort: high model: opus --- # x `); try { // Programmatic invocation const r = resolvePhaseSignalFromFile(fixture, 'plan'); assert.deepEqual(r, { effort: 'high', model: 'opus' }); // CLI shim const helperPath = new URL('../../lib/profiles/phase-signal-resolver.mjs', import.meta.url).pathname; const out = execFileSync('node', [helperPath, '--brief', fixture, '--phase', 'plan', '--json'], { encoding: 'utf-8', }); const parsed = JSON.parse(out.trim()); assert.deepEqual(parsed, { effort: 'high', model: 'opus' }); } finally { try { unlinkSync(fixture); } catch { /* swallow */ } } });