import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { validateBriefContent } from '../../lib/validators/brief-validator.mjs'; const GOOD_BRIEF = `--- type: trekbrief 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: trekbrief', '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')); }); const REVIEW_AS_BRIEF = `--- type: trekreview task: "Review delivered trekreview v1.0" slug: trekreview project_dir: .claude/projects/2026-05-01-trekreview/ findings: - 0123456789abcdef0123456789abcdef01234567 - fedcba9876543210fedcba9876543210fedcba98 --- # Review brief ## Intent Adversarial review of delivered trekreview v1.0. ## Goal Find what was missed. ## Success Criteria - All BLOCKER findings get a fix-plan. `; test('validateBrief — trekreview type accepted with findings array', () => { const r = validateBriefContent(REVIEW_AS_BRIEF, { strict: true }); assert.equal(r.valid, true, JSON.stringify(r.errors)); }); test('validateBrief — trekreview without findings rejected (BRIEF_MISSING_FIELD)', () => { const t = REVIEW_AS_BRIEF.replace(/findings:\n - 0123[\s\S]*?- fedcba[0-9a-f]+\n/, ''); const r = validateBriefContent(t); assert.equal(r.valid, false); assert.ok( r.errors.find(e => e.code === 'BRIEF_MISSING_FIELD' && /findings/.test(e.message)), `expected BRIEF_MISSING_FIELD for findings; got ${JSON.stringify(r.errors)}`, ); }); test('validateBrief — trekreview with findings as scalar (not array) rejected (BRIEF_BAD_FINDINGS_TYPE)', () => { const t = REVIEW_AS_BRIEF.replace( /findings:\n - 0123[\s\S]*?- fedcba[0-9a-f]+/, 'findings: not-an-array', ); const r = validateBriefContent(t); assert.equal(r.valid, false); assert.ok(r.errors.find(e => e.code === 'BRIEF_BAD_FINDINGS_TYPE')); }); test('validateBrief — wrong-type error message includes accepted set', () => { const t = REVIEW_AS_BRIEF.replace('type: trekreview', 'type: somethingelse'); const r = validateBriefContent(t); assert.equal(r.valid, false); const wrongType = r.errors.find(e => e.code === 'BRIEF_WRONG_TYPE'); assert.ok(wrongType); assert.ok(/trekbrief/.test(wrongType.message)); assert.ok(/trekreview/.test(wrongType.message)); });