Files
easyai-ai-gateway/tests/acceptance/report-test.mjs
T
wangbo 0b681275ed fix(acceptance): 支持显式跳过本地验收
在用户明确授权时,将本地原生与 amd64 制品阶段记录为 skipped/waived,并直接使用当前生产脱敏快照进入线上模拟。修复线上报告合并未写入生产 Run ID 导致后续 promote CAS 必然失败的问题。已通过报告回归测试、bash -n、ShellCheck 和 diff 检查。
2026-07-31 23:15:22 +08:00

159 lines
5.9 KiB
JavaScript

import assert from 'node:assert/strict';
import { execFileSync, spawnSync } from 'node:child_process';
import { mkdtempSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join, resolve } from 'node:path';
const root = resolve(import.meta.dirname, '../..');
const temporary = mkdtempSync(join(tmpdir(), 'easyai-acceptance-report-'));
const reports = join(temporary, 'reports');
mkdirSync(reports);
const releaseSha = 'a'.repeat(40);
const digest = `sha256:${'b'.repeat(64)}`;
const configHash = 'c'.repeat(64);
const snapshotHash = 'd'.repeat(64);
const runID = '00000000-0000-4000-8000-000000000001';
const writeJSON = (path, value) => writeFileSync(path, `${JSON.stringify(value)}\n`, { mode: 0o600 });
const runtime = join(temporary, 'runtime.json');
const snapshot = join(temporary, 'snapshot.json');
const artifact = join(temporary, 'artifact.json');
const output = join(temporary, 'acceptance-report.json');
const partialOutput = join(temporary, 'acceptance-report.partial.json');
const productionPartial = join(temporary, 'production-summary.partial.json');
const mergedPartialOutput = join(temporary, 'acceptance-report.production-partial.json');
const waivedOutput = join(temporary, 'acceptance-report.waived.json');
writeJSON(runtime, {
schemaVersion: 'acceptance-runtime/v1',
runId: runID,
releaseSha,
apiImageDigest: digest,
workerImageDigest: digest,
snapshotConfigHash: configHash,
snapshotSha256: snapshotHash,
apiKeys: ['must-not-be-copied'],
runToken: 'must-not-be-copied'
});
writeJSON(snapshot, {
schemaVersion: 'acceptance-snapshot/v1',
source: { releaseSha: 'e'.repeat(40), configHash },
snapshotSha256: snapshotHash
});
writeJSON(artifact, {
schemaVersion: 'acceptance-artifact-smoke/v1',
releaseSha,
apiImageDigest: digest,
passed: true,
secretSafe: true
});
writeJSON(join(reports, 'load.json'), {
schemaVersion: 'acceptance-load-report/v1',
profile: 'simulated-smoke',
passed: true,
secretSafe: true,
phases: [],
startedAt: '2026-01-01T00:00:00Z',
finishedAt: '2026-01-01T00:01:00Z'
});
execFileSync('node', [
join(root, 'scripts/acceptance/report.mjs'),
'build-local',
'--runtime', runtime,
'--snapshot', snapshot,
'--reports', reports,
'--artifact', artifact,
'--api-digest', digest,
'--certified-profile', 'P24',
'--output', output
]);
const report = JSON.parse(readFileSync(output, 'utf8'));
assert.equal(report.schemaVersion, 'acceptance-report/v1');
assert.equal(report.stages.localNative.status, 'passed');
assert.equal(report.stages.amd64Artifact.status, 'passed');
assert.equal(report.promotion.ready, false);
assert.equal(readFileSync(output, 'utf8').includes('must-not-be-copied'), false);
execFileSync('node', [join(root, 'scripts/acceptance/report.mjs'), 'validate', '--input', output]);
const overwrite = spawnSync('node', [
join(root, 'scripts/acceptance/report.mjs'),
'build-local',
'--runtime', runtime,
'--snapshot', snapshot,
'--reports', reports,
'--artifact', artifact,
'--api-digest', digest,
'--output', output
], { encoding: 'utf8' });
assert.notEqual(overwrite.status, 0);
assert.match(`${overwrite.stdout}${overwrite.stderr}`, /EEXIST|file already exists/);
assert.equal(JSON.parse(readFileSync(output, 'utf8')).runId, runID);
execFileSync('node', [
join(root, 'scripts/acceptance/report.mjs'),
'build-local-partial',
'--runtime', runtime,
'--snapshot', snapshot,
'--reports', reports,
'--certified-profile', 'P24',
'--failure-phase', 'capacity_p28_round_2',
'--failure-gate', 'local_control_plane_restarted',
'--output', partialOutput
]);
const partialReport = JSON.parse(readFileSync(partialOutput, 'utf8'));
assert.equal(partialReport.stages.localNative.status, 'failed');
assert.equal(partialReport.stages.localNative.failurePhase, 'capacity_p28_round_2');
assert.equal(partialReport.gates[0].id, 'local_control_plane_restarted');
assert.equal(partialReport.stages.amd64Artifact.status, 'not-run');
assert.equal(partialReport.promotion.ready, false);
assert.equal(readFileSync(partialOutput, 'utf8').includes('must-not-be-copied'), false);
execFileSync('node', [join(root, 'scripts/acceptance/report.mjs'), 'validate', '--input', partialOutput]);
writeJSON(productionPartial, {
runId: runID,
releaseSha,
apiImageDigest: digest,
snapshotConfigHash: configHash,
snapshotSha256: snapshotHash,
stableCapacityProfile: 'P24',
realCanaryPassed: false,
gates: [
{
id: 'workflow_interrupted',
passed: false,
detail: 'production acceptance interrupted by signal'
}
],
passed: false
});
execFileSync('node', [
join(root, 'scripts/acceptance/report.mjs'),
'merge-production',
'--local-report', output,
'--production-summary', productionPartial,
'--output', mergedPartialOutput
]);
const mergedPartial = JSON.parse(readFileSync(mergedPartialOutput, 'utf8'));
assert.equal(mergedPartial.stages.onlineSimulation.status, 'failed');
assert.equal(mergedPartial.runId, runID);
assert.equal(mergedPartial.stages.realCanary.status, 'failed');
assert.equal(mergedPartial.promotion.ready, false);
assert.equal(mergedPartial.promotion.trafficMode, 'validation');
assert.equal(mergedPartial.gates.at(-1).id, 'workflow_interrupted');
execFileSync('node', [join(root, 'scripts/acceptance/report.mjs'), 'validate', '--input', mergedPartialOutput]);
execFileSync('node', [
join(root, 'scripts/acceptance/report.mjs'),
'build-online-waiver',
'--release-sha', releaseSha,
'--api-digest', digest,
'--snapshot', snapshot,
'--waiver-reason', 'user_directed_online_only_acceptance',
'--output', waivedOutput
]);
const waived = JSON.parse(readFileSync(waivedOutput, 'utf8'));
assert.equal(waived.stages.localNative.status, 'skipped');
assert.equal(waived.stages.amd64Artifact.status, 'skipped');
assert.equal(waived.gates[0].id, 'local_acceptance_explicitly_waived');
assert.equal(waived.promotion.ready, false);
execFileSync('node', [join(root, 'scripts/acceptance/report.mjs'), 'validate', '--input', waivedOutput]);