fix(release): gate channel and distribution acceptance
This commit is contained in:
@@ -51,6 +51,7 @@ function validApproval() {
|
||||
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),
|
||||
}]
|
||||
@@ -84,17 +85,51 @@ function validApproval() {
|
||||
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,
|
||||
}
|
||||
}
|
||||
@@ -123,6 +158,8 @@ test('audits the checked-in pending manifest without claiming release readiness'
|
||||
assert.equal(audit.status, 0, audit.stderr)
|
||||
assert.match(audit.stdout, /approved scenarios: 0\/5/)
|
||||
assert.match(audit.stdout, /approved policy questions: 0\/30/)
|
||||
assert.match(audit.stdout, /approved direct channels: 0\/5/)
|
||||
assert.match(audit.stdout, /controlled distribution: PENDING/)
|
||||
|
||||
const strict = spawnSync(process.execPath, [verifierPath, '--strict', pendingApprovalPath], {
|
||||
cwd: projectRoot,
|
||||
@@ -168,6 +205,18 @@ test('rejects incomplete or contradictory formal approvals', async (t) => {
|
||||
['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) {
|
||||
|
||||
Reference in New Issue
Block a user