import assert from 'node:assert/strict' import { createHash } from 'node:crypto' import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' import os from 'node:os' import path from 'node:path' import { fileURLToPath } from 'node:url' import { spawnSync } from 'node:child_process' import test from 'node:test' const testDir = path.dirname(fileURLToPath(import.meta.url)) const projectRoot = path.resolve(testDir, '..', '..') const verifierPath = path.join(projectRoot, 'scripts', 'verify-aug1-formal-content.mjs') const approvalPath = path.join( projectRoot, 'docs', 'content-candidates', 'aug1-release-approval.json', ) const candidatePath = path.join( projectRoot, 'docs', 'content-candidates', 'aug1-life-advisor-content-candidates-v0.1.json', ) const candidateBytes = await readFile(candidatePath) const candidate = JSON.parse(candidateBytes.toString('utf8')) const candidateSha256 = createHash('sha256').update(candidateBytes).digest('hex') const source = { sourceId: 'FORMAL-OPS-001', title: '现行生活顾问服务作业标准', version: '2026.07', effectiveDate: '2026-07-01', scope: '受控内测项目', sha256: 'a'.repeat(64), formalPolicy: true, } function validApproval() { const scenarioApprovals = Object.fromEntries(candidate.scenarios.map((item, index) => { const highRisk = item.proposedRiskLevel === '高风险' return [item.candidateId, { status: 'APPROVED', scenarioCode: item.scenarioDraft.id, riskLevel: item.proposedRiskLevel, sourceRefs: ['FORMAL-OPS-001:section-1'], reviewedBy: `reviewer-${index + 1}`, reviewedAt: '2026-07-30T09:00:00+08:00', ...(highRisk ? { secondReviewedBy: `second-reviewer-${index + 1}`, secondReviewedAt: '2026-07-30T10:00:00+08:00', } : {}), businessEvidenceRef: `evidence/scenario-${index + 1}.pdf`, businessEvidenceSha256: 'c'.repeat(64), productionContentVersion: `aug1-v${index + 1}`, productionContentHash: String(index + 1).repeat(64), }] })) const policyQuestionApprovals = Object.fromEntries(candidate.policyQuestionCandidates.map((item, index) => { const answerDisposition = item.category === 'CONFLICT' ? 'FORMAL_CONFLICT_BOUNDARY' : item.category === 'NO_EVIDENCE' ? 'FORMAL_NO_EVIDENCE_BOUNDARY' : item.category === 'UNAUTHORIZED' ? 'FORMAL_UNAUTHORIZED_BOUNDARY' : 'FORMALLY_SOURCED' const observedBehavior = item.category === 'CONFLICT' ? 'CONFLICT_BOUNDARY_DETECTED' : item.category === 'NO_EVIDENCE' ? 'NO_EVIDENCE_BOUNDARY_DETECTED' : item.category === 'UNAUTHORIZED' ? 'UNAUTHORIZED_BOUNDARY_DETECTED' : '验收结果与预期边界一致' return [item.id, { status: 'APPROVED', answerDisposition, sourceRefs: answerDisposition === 'FORMALLY_SOURCED' ? ['FORMAL-OPS-001:section-2'] : [], expectedBehavior: item.expectedBehavior, observedBehavior, evidenceRef: `evidence/question-${index + 1}.json`, evidenceSha256: 'b'.repeat(64), reviewedBy: `question-reviewer-${index + 1}`, reviewedAt: '2026-07-30T11:00:00+08:00', }] })) const signoffs = Object.fromEntries([ 'businessOwner', 'knowledgeOwner', 'trainingOwner', 'channelOwner', 'releaseOwner', ].map((role) => [role, { status: 'APPROVED', reviewedBy: `${role}-reviewer`, reviewedAt: '2026-07-30T12:00:00+08:00', }])) const channelApprovals = Object.fromEntries([ 'direct_president', 'direct_finance', 'direct_hr', 'direct_audit', 'direct_operations', ].map((role) => [role, { status: 'APPROVED', minimumActiveHandlers: 2, bindingEvidenceRef: `evidence/${role}-binding.json`, bindingEvidenceSha256: 'd'.repeat(64), positiveAccessEvidenceRef: `evidence/${role}-positive.json`, positiveAccessEvidenceSha256: 'e'.repeat(64), crossChannelDenialEvidenceRef: `evidence/${role}-denial.json`, crossChannelDenialEvidenceSha256: 'f'.repeat(64), singleReplyEvidenceRef: `evidence/${role}-reply.json`, singleReplyEvidenceSha256: '1'.repeat(64), reviewedBy: `${role}-reviewer`, reviewedAt: '2026-07-30T11:30:00+08:00', }])) const expectedApkSignerSha256 = '2'.repeat(64) return { schemaVersion: '1.0', releaseTarget: '2026-08-01', tenantId: '000000', candidateFile: 'aug1-life-advisor-content-candidates-v0.1.json', candidateSha256, expectedApkSignerSha256, releaseStatus: 'APPROVED', formalContentVersion: 'aug1-formal-v1', sourceRegistry: [source], scenarioApprovals, policyQuestionApprovals, channelApprovals, distributionApproval: { status: 'APPROVED', scope: 'CONTROLLED_INTERNAL_TEST', legalDecision: 'CONTROLLED_INTERNAL_TEST_EXCEPTION_ACCEPTED', signerDecision: 'DCLOUD_TEST_SIGNER_ACCEPTED_FOR_CONTROLLED_INTERNAL_TEST', signerSha256: expectedApkSignerSha256, distributionEvidenceRef: 'evidence/controlled-distribution.json', distributionEvidenceSha256: '3'.repeat(64), reviewedBy: 'distribution-reviewer', reviewedAt: '2026-07-30T12:30:00+08:00', }, signoffs, } } async function verifyApproval(approval, ...options) { const tempDir = await mkdtemp(path.join(os.tmpdir(), 'aug1-formal-')) const tempCandidatePath = path.join(tempDir, 'aug1-life-advisor-content-candidates-v0.1.json') const tempApprovalPath = path.join(tempDir, 'approval.json') try { await writeFile(tempCandidatePath, candidateBytes) await writeFile(tempApprovalPath, JSON.stringify(approval), 'utf8') return spawnSync(process.execPath, [verifierPath, ...options, tempApprovalPath], { cwd: projectRoot, encoding: 'utf8', }) } finally { await rm(tempDir, { recursive: true, force: true }) } } test('audits the checked-in controlled-test approval and passes strict verification', () => { const audit = spawnSync(process.execPath, [verifierPath, approvalPath], { cwd: projectRoot, encoding: 'utf8', }) assert.equal(audit.status, 0, audit.stderr) assert.match(audit.stdout, /approved scenarios: 5\/5/) assert.match(audit.stdout, /approved policy questions: 30\/30/) assert.match(audit.stdout, /approved direct channels: 5\/5/) assert.match(audit.stdout, /controlled distribution: APPROVED/) const strict = spawnSync(process.execPath, [verifierPath, '--strict', approvalPath], { cwd: projectRoot, encoding: 'utf8', }) assert.equal(strict.status, 0, strict.stderr) assert.match(strict.stdout, /strict release ready: true/) }) test('accepts complete formal evidence and emits five immutable production snapshots', async () => { const strict = await verifyApproval(validApproval(), '--strict') assert.equal(strict.status, 0, strict.stderr) assert.match(strict.stdout, /strict release ready: true/) const snapshots = await verifyApproval(validApproval(), '--scenario-snapshots') assert.equal(snapshots.status, 0, snapshots.stderr) const lines = snapshots.stdout.trim().split(/\r?\n/) assert.equal(lines.length, 5) assert.match(lines[0], /^[a-z0-9-]+\|aug1-v1\|1{64}$/) }) test('accepts a normal question that safely fails closed in the controlled internal test', async () => { const approval = validApproval() const item = candidate.policyQuestionCandidates.find((question) => question.category === 'NORMAL') approval.policyQuestionApprovals[item.id].answerDisposition = 'FORMAL_NO_EVIDENCE_BOUNDARY' approval.policyQuestionApprovals[item.id].sourceRefs = [] approval.policyQuestionApprovals[item.id].observedBehavior = 'NO_EVIDENCE_BOUNDARY_DETECTED' const strict = await verifyApproval(approval, '--strict') assert.equal(strict.status, 0, strict.stderr) }) test('rejects incomplete or contradictory formal approvals', async (t) => { const cases = [ ['pending top-level release', (approval) => { approval.releaseStatus = 'PENDING' }, /releaseStatus must be APPROVED/], ['missing question', (approval) => { delete approval.policyQuestionApprovals[candidate.policyQuestionCandidates[0].id] }, /question approvals must cover exactly 30\/30/], ['wrong no-evidence behavior', (approval) => { const item = candidate.policyQuestionCandidates.find((question) => question.category === 'NO_EVIDENCE') approval.policyQuestionApprovals[item.id].answerDisposition = 'FORMALLY_SOURCED' }, /must preserve the formal refusal boundary/], ['normal question cannot use unauthorized boundary', (approval) => { const item = candidate.policyQuestionCandidates.find((question) => question.category === 'NORMAL') approval.policyQuestionApprovals[item.id].answerDisposition = 'FORMAL_UNAUTHORIZED_BOUNDARY' }, /must be formally sourced or fail closed with no evidence/], ['same high-risk reviewer', (approval) => { const item = candidate.scenarios.find((scenario) => scenario.proposedRiskLevel === '高风险') approval.scenarioApprovals[item.candidateId].secondReviewedBy = approval.scenarioApprovals[item.candidateId].reviewedBy }, /high-risk reviewers must be different people/], ['unknown source', (approval) => { const item = candidate.scenarios[0] approval.scenarioApprovals[item.candidateId].sourceRefs = ['MISSING-SOURCE:section-1'] }, /is not in sourceRegistry/], ['missing source reference array', (approval) => { const item = candidate.scenarios[0] delete approval.scenarioApprovals[item.candidateId].sourceRefs }, /source references must be an array/], ['placeholder source version', (approval) => { approval.sourceRegistry[0].version = '待填写' }, /formal source version is required/], ['missing owner sign-off', (approval) => { approval.signoffs.releaseOwner.status = 'PENDING' }, /releaseOwner: sign-off must be APPROVED/], ['missing channel acceptance', (approval) => { delete approval.channelApprovals.direct_audit }, /channel approvals must cover exactly 5\/5 roles/], ['channel without backup requirement', (approval) => { approval.channelApprovals.direct_finance.minimumActiveHandlers = 1 }, /primary and backup requirement must remain 2/], ['unaccepted distribution', (approval) => { approval.distributionApproval.status = 'PENDING' }, /distribution approval must be APPROVED/], ['mismatched APK signer', (approval) => { approval.distributionApproval.signerSha256 = '4'.repeat(64) }, /distribution signer SHA-256 must match the expected APK signer/], ] for (const [name, mutate, expected] of cases) { await t.test(name, async () => { const approval = validApproval() mutate(approval) const result = await verifyApproval(approval, '--strict') assert.notEqual(result.status, 0) assert.match(result.stderr, expected) }) } })