import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { validateBriefContent } from '../../lib/validators/brief-validator.mjs'; const GOOD_BRIEF = `--- type: ultrabrief brief_version: "2.0" created: 2026-04-30 task: "Add JWT auth to API" slug: jwt-auth project_dir: .claude/projects/2026-04-30-jwt-auth/ research_topics: 2 research_status: pending auto_research: false interview_turns: 5 source: interview --- # Task: JWT auth ## Intent Why this matters. ## Goal What success looks like. ## Success Criteria - All tests pass. `; test('validateBrief — happy path', () => { const r = validateBriefContent(GOOD_BRIEF, { strict: true }); assert.equal(r.valid, true, JSON.stringify(r.errors)); }); test('validateBrief — wrong type rejected', () => { const t = GOOD_BRIEF.replace('type: ultrabrief', 'type: notabrief'); const r = validateBriefContent(t); assert.equal(r.valid, false); assert.ok(r.errors.find(e => e.code === 'BRIEF_WRONG_TYPE')); }); test('validateBrief — missing required field', () => { const t = GOOD_BRIEF.replace(/^research_topics: 2\n/m, ''); const r = validateBriefContent(t); assert.equal(r.valid, false); assert.ok(r.errors.find(e => e.code === 'BRIEF_MISSING_FIELD' && /research_topics/.test(e.message))); }); test('validateBrief — bad research_status value', () => { const t = GOOD_BRIEF.replace('research_status: pending', 'research_status: maybe'); const r = validateBriefContent(t); assert.equal(r.valid, false); assert.ok(r.errors.find(e => e.code === 'BRIEF_BAD_STATUS')); }); test('validateBrief — state machine: research_topics > 0 + skipped without partial = error', () => { const t = GOOD_BRIEF.replace('research_status: pending', 'research_status: skipped'); const r = validateBriefContent(t); assert.equal(r.valid, false); assert.ok(r.errors.find(e => e.code === 'BRIEF_STATE_INCOHERENT')); }); test('validateBrief — state machine: skipped + brief_quality: partial = warning only', () => { const t = GOOD_BRIEF .replace('research_status: pending', 'research_status: skipped') .replace('source: interview', 'source: interview\nbrief_quality: partial'); const r = validateBriefContent(t); assert.equal(r.valid, true, JSON.stringify(r.errors)); assert.ok(r.warnings.find(w => w.code === 'BRIEF_PARTIAL_SKIPPED')); }); test('validateBrief — strict requires body sections', () => { const t = GOOD_BRIEF.replace(/## Intent\n\nWhy this matters\.\n\n/, ''); const r = validateBriefContent(t, { strict: true }); assert.equal(r.valid, false); assert.ok(r.errors.find(e => e.code === 'BRIEF_MISSING_SECTION')); }); test('validateBrief — soft demotes section errors to warnings', () => { const t = GOOD_BRIEF.replace(/## Goal\n\nWhat success looks like\.\n\n/, ''); const r = validateBriefContent(t, { strict: false }); assert.equal(r.valid, true); assert.ok(r.warnings.find(w => w.code === 'BRIEF_MISSING_SECTION')); }); test('validateBrief — missing frontmatter is hard error', () => { const r = validateBriefContent('# just markdown\n\nno frontmatter\n'); assert.equal(r.valid, false); assert.ok(r.errors.find(e => e.code === 'FM_MISSING')); });