import assert from 'node:assert/strict'; import test from 'node:test'; import { loadScenarioCatalog } from './catalog.mjs'; import { createGateReport, validateGateReport, } from './report.mjs'; const COMMIT = 'a'.repeat(40); const ARTIFACT = 'b'.repeat(64); async function passingReport() { const catalog = await loadScenarioCatalog(); return createGateReport({ commitSha: COMMIT, branch: 'main', artifactSha256: ARTIFACT, artifact: { path: '.runtime/portal', kind: 'directory-tree' }, scenarios: catalog.map((scenario) => ({ id: scenario.id, name: scenario.name, status: 'passed', cleanup_status: 'not_required', evidence: ['fixture'], })), completedAt: new Date('2026-07-26T10:00:00.000Z'), }); } test('complete report bound to main commit and artifact passes validation', async () => { const report = await passingReport(); const result = validateGateReport(report, { expectedCommit: COMMIT, expectedArtifactSha256: ARTIFACT, now: new Date('2026-07-26T11:00:00.000Z'), }); assert.deepEqual(result, { valid: true, errors: [] }); }); test('unknown scenario and mismatched summary fail closed', async () => { const report = await passingReport(); report.scenarios[0].status = 'unknown'; const result = validateGateReport(report, { expectedCommit: COMMIT, expectedArtifactSha256: ARTIFACT, now: new Date('2026-07-26T11:00:00.000Z'), }); assert.equal(result.valid, false); assert.match(result.errors.join('\n'), /REL-01 is unknown/); assert.match(result.errors.join('\n'), /summary does not match/); }); test('non-exemptable scenario cannot be marked not_applicable', async () => { const report = await passingReport(); const scenario = report.scenarios.find((item) => item.id === 'COMP-09'); scenario.status = 'not_applicable'; scenario.exemption = { scenario_id: 'COMP-09', commit_sha: COMMIT, reason: 'fixture', changed_paths_checked: ['README.md'], dependency_evidence: ['none'], reviewed_by: 'reviewer', reviewed_at: '2026-07-26T10:00:00.000Z', }; report.summary.passed -= 1; report.summary.not_applicable += 1; const result = validateGateReport(report, { expectedCommit: COMMIT, expectedArtifactSha256: ARTIFACT, now: new Date('2026-07-26T11:00:00.000Z'), }); assert.equal(result.valid, false); assert.match(result.errors.join('\n'), /COMP-09 is never exemptable/); }); test('reviewed exemption is accepted only for an exemptable scenario', async () => { const report = await passingReport(); const scenario = report.scenarios.find((item) => item.id === 'SEARCH-04'); scenario.status = 'not_applicable'; scenario.exemption = { scenario_id: 'SEARCH-04', commit_sha: COMMIT, reason: 'GitHub Search is disabled and no shared gateway path changed', changed_paths_checked: ['docs/example.md'], dependency_evidence: ['impact-map.json'], reviewed_by: 'independent-reviewer', reviewed_at: '2026-07-26T10:00:00.000Z', }; report.exemptions = [scenario.exemption]; report.summary.passed -= 1; report.summary.not_applicable += 1; const result = validateGateReport(report, { expectedCommit: COMMIT, expectedArtifactSha256: ARTIFACT, now: new Date('2026-07-26T11:00:00.000Z'), }); assert.deepEqual(result, { valid: true, errors: [] }); }); test('expired report or artifact mismatch is rejected', async () => { const report = await passingReport(); const result = validateGateReport(report, { expectedCommit: COMMIT, expectedArtifactSha256: 'c'.repeat(64), now: new Date('2026-07-27T11:00:00.000Z'), }); assert.equal(result.valid, false); assert.match(result.errors.join('\n'), /artifact_sha256 does not match/); assert.match(result.errors.join('\n'), /report has expired/); });