test: require launch persona evidence

This commit is contained in:
Codex
2026-07-01 08:21:33 +08:00
parent 1c94e6f966
commit 9d1bb47041
5 changed files with 98 additions and 1 deletions

View File

@@ -16,6 +16,7 @@ function sampleSummaryValue(expectedValue) {
if (!expectedValue || typeof expectedValue !== 'object' || Array.isArray(expectedValue)) return expectedValue;
if (Object.prototype.hasOwnProperty.call(expectedValue, 'eq')) return expectedValue.eq;
if (Object.prototype.hasOwnProperty.call(expectedValue, 'oneOf')) return expectedValue.oneOf?.[0];
if (Object.prototype.hasOwnProperty.call(expectedValue, 'truthy')) return expectedValue.truthy ? 'sample-truthy-value' : '';
if (Object.prototype.hasOwnProperty.call(expectedValue, 'lte')) return expectedValue.lte;
if (Object.prototype.hasOwnProperty.call(expectedValue, 'lt')) return Number(expectedValue.lt) - 1;
if (Object.prototype.hasOwnProperty.call(expectedValue, 'gte')) return expectedValue.gte;
@@ -176,6 +177,29 @@ assert.ok(
'slow mixed benchmark should be reported as a blocker',
);
const missingLaunchPersonaSmoke = runGate(tempDir => {
const evidence = createEvidence(tempDir);
evidence.checks = evidence.checks.filter(item => item.id !== 'api.launch-persona-smoke');
return evidence;
});
assert.notEqual(missingLaunchPersonaSmoke.status, 0, 'missing launch persona smoke should fail launch gate');
assert.ok(
missingLaunchPersonaSmoke.payload.checks?.some(item => item.id === 'check.api.launch-persona-smoke' && item.status === 'blocker'),
'missing launch persona smoke should be reported as a blocker',
);
const launchPersonaWithoutSvip = runGate(tempDir => {
const evidence = createEvidence(tempDir);
const item = evidence.checks.find(check => check.id === 'api.launch-persona-smoke');
item.summary.student.result.entitlement.isSvip = false;
return evidence;
});
assert.notEqual(launchPersonaWithoutSvip.status, 0, 'launch persona smoke without SVIP should fail launch gate');
assert.ok(
launchPersonaWithoutSvip.payload.checks?.some(item => item.id === 'check.api.launch-persona-smoke.summary' && item.status === 'blocker'),
'launch persona SVIP failure should be reported as a blocker',
);
const missingBusinessSampling = runGate(tempDir => {
const evidence = createEvidence(tempDir);
evidence.checks = evidence.checks.filter(item => item.id !== 'migration.pb-import-sample');

View File

@@ -96,6 +96,25 @@ const gateChecks = [
includeWrites: true,
},
},
{
id: 'api.launch-persona-smoke',
label: 'Real API launch persona journey smoke',
commandIncludes: 'smoke:launch-persona',
summary: {
status: 'pass',
'student.status': 'pass',
'student.result.entitlement.isSvip': true,
'student.result.practice.questionCount': { gte: 1 },
'student.result.favorite.favoriteCount': { gte: 1 },
'student.result.favorite.reviewSessionId': { truthy: true },
'student.result.wrongReview.reviewPlanCount': { gte: 0 },
'tenantAdmin.status': 'pass',
'tenantAdmin.result.guards.studentDashboardDenied': { oneOf: ['TENANT_MEMBER_REQUIRED', 'TENANT_PERMISSION_DENIED', 'FORBIDDEN'] },
'tenantAdmin.result.guards.crossTenantDenied': { oneOf: ['TENANT_MEMBER_REQUIRED', 'TENANT_PERMISSION_DENIED', 'FORBIDDEN'] },
'platformAdmin.status': 'pass',
'platformAdmin.result.guards.studentPlatformDenied': { oneOf: ['PLATFORM_ADMIN_REQUIRED', 'FORBIDDEN'] },
},
},
{
id: 'api.integration',
label: 'API integration regression',
@@ -342,7 +361,7 @@ function compareSummary(actualSummary, expectedSummary) {
function isComparatorSpec(value) {
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
return ['eq', 'lt', 'lte', 'gt', 'gte', 'oneOf'].some(key => Object.prototype.hasOwnProperty.call(value, key));
return ['eq', 'lt', 'lte', 'gt', 'gte', 'oneOf', 'truthy'].some(key => Object.prototype.hasOwnProperty.call(value, key));
}
function compareExpectedValue(actualValue, expectedValue) {
@@ -362,6 +381,12 @@ function compareExpectedValue(actualValue, expectedValue) {
return `expected one of ${JSON.stringify(choices)} but got ${JSON.stringify(actualValue)}`;
}
}
if (Object.prototype.hasOwnProperty.call(expectedValue, 'truthy')) {
const shouldBeTruthy = Boolean(expectedValue.truthy);
if (Boolean(actualValue) !== shouldBeTruthy) {
return `expected ${shouldBeTruthy ? 'truthy' : 'falsy'} but got ${JSON.stringify(actualValue)}`;
}
}
const numericChecks = [
['lt', (actual, expected) => actual < expected, '<'],