/** * Smoke test for verdict-recorder: confirm end-to-end startRun → * setScenarioContext → recordVerdict → endRun writes a well-formed * summary report. */ import { test } from 'node:test'; import assert from 'node:assert/strict'; import * as fs from 'node:fs/promises'; import * as os from 'node:os'; import * as path from 'node:path'; import { startRun, setScenarioContext, recordVerdict, endRun, } from '../src/verdict-recorder.js'; test('startRun → recordVerdict → endRun writes summary with total/passed', async () => { const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'cabinet-verify-smoke-')); const originalCwd = process.cwd(); process.chdir(tmpDir); try { const runId = await startRun(); assert.match(runId, /^run-\d{4}-\d{2}-\d{2}T/); setScenarioContext('test.feature', 'Smoke', 'user'); await recordVerdict({ checkId: '0.01', stepText: 'smoke', verdict: 'auto:pass', source: 'auto', durationMs: 5, }); const summary = await endRun(); assert.equal(summary.total, 1); assert.equal(summary.passed, 1); assert.equal(summary.failed, 0); assert.equal(summary.humanVerdicts, 0); const summaryPath = path.join(tmpDir, 'reports', `${runId}.summary.json`); const summaryStat = await fs.stat(summaryPath); assert.ok(summaryStat.isFile(), 'summary.json should exist'); const summaryContent = JSON.parse(await fs.readFile(summaryPath, 'utf8')); assert.equal(summaryContent.total, 1); assert.equal(summaryContent.passed, 1); assert.ok(Array.isArray(summaryContent.rows), 'summary should include rows array'); assert.equal(summaryContent.rows.length, 1); assert.equal(summaryContent.rows[0].checkId, '0.01'); } finally { process.chdir(originalCwd); await fs.rm(tmpDir, { recursive: true, force: true }); } }); test('claude-source verdicts count in pass/fail/subjective tallies', async () => { const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'cabinet-verify-claude-')); const originalCwd = process.cwd(); process.chdir(tmpDir); try { await startRun(); setScenarioContext('test.feature', 'Claude standin', 'user'); // claude:P and claude:N count as passed; claude:I as failed; all three // (plus the human row) count as subjective verdicts. await recordVerdict({ checkId: '1.01', stepText: 'a', verdict: 'claude:P', source: 'claude', durationMs: 5, pathHash: 'a' }); await recordVerdict({ checkId: '1.02', stepText: 'b', verdict: 'claude:N', source: 'claude', durationMs: 5, pathHash: 'b' }); await recordVerdict({ checkId: '1.03', stepText: 'c', verdict: 'claude:I', source: 'claude', durationMs: 5, pathHash: 'c' }); await recordVerdict({ checkId: '1.04', stepText: 'd', verdict: 'human:P', source: 'human', durationMs: 5, pathHash: 'd' }); await recordVerdict({ checkId: '1.05', stepText: 'e', verdict: 'auto:pass', source: 'auto', durationMs: 1 }); const summary = await endRun(); assert.equal(summary.total, 5); // passed: claude:P, claude:N, human:P, auto:pass assert.equal(summary.passed, 4); // failed: claude:I assert.equal(summary.failed, 1); // subjective (humanVerdicts): 3 claude + 1 human, excludes auto assert.equal(summary.humanVerdicts, 4); } finally { process.chdir(originalCwd); await fs.rm(tmpDir, { recursive: true, force: true }); } }); test('invalid source is rejected by recordVerdict', async () => { const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), 'cabinet-verify-badsrc-')); const originalCwd = process.cwd(); process.chdir(tmpDir); try { await startRun(); setScenarioContext('test.feature', 'Bad source', 'user'); await assert.rejects( // @ts-expect-error — deliberately invalid source value () => recordVerdict({ checkId: '2.01', stepText: 'x', verdict: 'bot:P', source: 'bot', durationMs: 1 }), /invalid source/, ); } finally { process.chdir(originalCwd); await fs.rm(tmpDir, { recursive: true, force: true }); } });