import { test } from 'node:test'; import { strict as assert } from 'node:assert'; import { validateReviewContent } from '../../lib/validators/review-validator.mjs'; const GOOD_REVIEW = `--- type: ultrareview review_version: "1.0" created: 2026-05-01 task: "Add JWT auth" slug: jwt-auth project_dir: .claude/projects/2026-04-30-jwt-auth/ brief_path: .claude/projects/2026-04-30-jwt-auth/brief.md scope_sha_start: abc123 scope_sha_end: def456 reviewed_files_count: 7 findings: - 0123456789abcdef0123456789abcdef01234567 - fedcba9876543210fedcba9876543210fedcba98 --- # Review ## Executive Summary Verdict: ALLOW. ## Coverage | File | Treatment | Reason | |------|-----------|--------| | lib/foo.mjs | deep-review | risk | ## Remediation Summary None. `; test('validateReview — happy path', () => { const r = validateReviewContent(GOOD_REVIEW, { strict: true }); assert.equal(r.valid, true, JSON.stringify(r.errors)); }); test('validateReview — wrong type rejected (REVIEW_WRONG_TYPE)', () => { const t = GOOD_REVIEW.replace('type: ultrareview', 'type: ultrabrief'); const r = validateReviewContent(t); assert.equal(r.valid, false); assert.ok(r.errors.find(e => e.code === 'REVIEW_WRONG_TYPE')); }); test('validateReview — missing required field (REVIEW_MISSING_FIELD)', () => { const t = GOOD_REVIEW.replace(/^brief_path: .*\n/m, ''); const r = validateReviewContent(t); assert.equal(r.valid, false); assert.ok(r.errors.find(e => e.code === 'REVIEW_MISSING_FIELD' && /brief_path/.test(e.message))); }); test('validateReview — missing required body section in strict (REVIEW_MISSING_SECTION)', () => { const t = GOOD_REVIEW.replace(/## Coverage[\s\S]*?(?=## Remediation)/m, ''); const r = validateReviewContent(t, { strict: true }); assert.equal(r.valid, false); assert.ok(r.errors.find(e => e.code === 'REVIEW_MISSING_SECTION' && /Coverage/.test(e.message))); }); test('validateReview — Coverage section is REQUIRED (no soft demotion to make Coverage optional)', () => { const t = GOOD_REVIEW.replace(/## Coverage[\s\S]*?(?=## Remediation)/m, ''); const r = validateReviewContent(t, { strict: true }); assert.equal(r.valid, false); }); test('validateReview — soft mode demotes section errors to warnings', () => { const t = GOOD_REVIEW.replace(/## Remediation Summary[\s\S]*$/m, ''); const r = validateReviewContent(t, { strict: false }); assert.equal(r.valid, true); assert.ok(r.warnings.find(w => w.code === 'REVIEW_MISSING_SECTION')); }); test('validateReview — missing frontmatter is hard error (FM_MISSING)', () => { const r = validateReviewContent('# review\n\nno frontmatter\n'); assert.equal(r.valid, false); assert.ok(r.errors.find(e => e.code === 'FM_MISSING')); }); test('validateReview — findings not an array → REVIEW_BAD_FINDINGS_TYPE', () => { // Replace block-style list with scalar → parser yields string const t = GOOD_REVIEW.replace( /findings:\n - 0123[\s\S]*?- fedcba[0-9a-f]+/, 'findings: not-an-array', ); const r = validateReviewContent(t); assert.equal(r.valid, false); assert.ok( r.errors.find(e => e.code === 'REVIEW_BAD_FINDINGS_TYPE'), `expected REVIEW_BAD_FINDINGS_TYPE, got: ${JSON.stringify(r.errors)}`, ); }); test('validateReview — finding-ID not 40-char hex → REVIEW_BAD_FINDING_ID', () => { const t = GOOD_REVIEW.replace( '0123456789abcdef0123456789abcdef01234567', 'NOT-A-VALID-HEX-ID', ); const r = validateReviewContent(t); assert.equal(r.valid, false); assert.ok(r.errors.find(e => e.code === 'REVIEW_BAD_FINDING_ID')); }); test('validateReview — empty findings array is acceptable (no findings = ALLOW verdict)', () => { const t = GOOD_REVIEW.replace( /findings:\n - 0123[\s\S]*?- fedcba[0-9a-f]+/, 'findings: []', ); const r = validateReviewContent(t); assert.equal(r.valid, true, JSON.stringify(r.errors)); });