fix(playable): close release validation lifecycle gaps

This commit is contained in:
shen
2026-09-11 21:23:44 +08:00
parent f39a55fdd5
commit cc5573ddc1
13 changed files with 230 additions and 7 deletions
+46
View File
@@ -0,0 +1,46 @@
#!/usr/bin/env node
// Release aggregation must reject the intermediate report before the RSS seal.
import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
import { fileURLToPath } from 'node:url';
const repo = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'mt-playable-release-'));
const write = (name, value) => fs.writeFileSync(path.join(root, name), JSON.stringify(value));
const fixture = JSON.parse(fs.readFileSync(path.join(repo, 'test/playable/report.valid.json'), 'utf8'));
const manifest = { schema_version: 1, candidate: fixture.build, required_runs: { soak: 1 },
runs: [{ run_id: fixture.run_id, suite: 'soak', path: 'run-1' }] };
const report = { ...fixture, suite: 'soak', exit_gate: { checked: true, process_code: 0, timed_out: false, errors: [] } };
const baseline = process.argv.includes('--baseline')
? spawnSync('git', ['show', 'f39a55fd:script/validate_playable_report.mjs'], { cwd: repo, encoding: 'utf8' }).stdout : null;
function check(expected, label) {
const args = baseline ? ['--input-type=module', '-'] : [path.join(repo, 'script/validate_playable_report.mjs')];
const result = spawnSync(process.execPath, [...args, '--release-dir', root], { input: baseline, encoding: 'utf8' });
assert.equal(result.status, expected, `${label}: ${result.stdout} ${result.stderr}`);
assert.equal(JSON.parse(fs.readFileSync(path.join(root, 'release-report.json'))).status, expected === 0 ? 'PASS' : 'FAIL');
console.log(`ok - ${label}`);
}
try {
fs.mkdirSync(path.join(root, 'run-1'));
write('release-manifest.json', manifest);
write('run-1/report.json', report);
check(1, 'soak PASS without external memory seal is rejected');
report.runner_cases = [{ id: 'STB-MEMORY-01', status: 'PASS' }];
write('run-1/report.json', report);
check(1, 'memory case alone cannot replace the verdict');
report.memory = { verdict: { status: 'FAIL' } };
write('run-1/report.json', report);
check(1, 'contradictory memory verdict is rejected');
report.memory.verdict.status = 'PASS';
write('run-1/report.json', report);
check(0, 'clean exit and external memory PASS can be aggregated');
write('run-1/report.json', null);
check(1, 'null run report produces a sealed release failure');
write('release-manifest.json', null);
check(1, 'null manifest produces a sealed release failure');
} finally {
fs.rmSync(root, { recursive: true, force: true });
}