mirror of
https://github.com/affaan-m/ECC.git
synced 2026-09-28 20:45:11 +02:00
test(plan-canvas): bound stalled cleanup and flush results before exit
Preserve contributor history while applying the exact reviewed current-main repair.
Source-PR: https://github.com/affaan-m/ECC/pull/3241
Source-Parent: 63f30ffc36
Review-Manifest-SHA256: 281ec1d297672ff9986406c47e210b41590e00926b8da5071eceeedef8c51833
This commit is contained in:
@@ -67,6 +67,69 @@ function printResults(results) {
|
||||
process.exitCode = results.failed > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
// A failed or stalled close may leave handles alive. Bound the whole async
|
||||
// run, then drain both output queues before exiting so the summary survives.
|
||||
// A blocked event loop or broken output sink still needs an outer watchdog.
|
||||
async function runTestProcess(run, suite, { timeoutMs = 60_000, flushTimeoutMs = 2_000 } = {}) {
|
||||
process.exitCode = 1;
|
||||
let deadline;
|
||||
const timeout = new Promise(resolve => {
|
||||
deadline = setTimeout(() => resolve({
|
||||
failed: true,
|
||||
error: new Error(`Plan Canvas test deadline exceeded (${timeoutMs}ms); setup, test, or cleanup did not settle`),
|
||||
}), timeoutMs);
|
||||
});
|
||||
const execution = Promise.resolve().then(run).then(
|
||||
() => ({ failed: false }),
|
||||
error => ({ failed: true, error })
|
||||
);
|
||||
const outcome = await Promise.race([execution, timeout]);
|
||||
clearTimeout(deadline);
|
||||
const results = {
|
||||
...suite.results,
|
||||
failed: suite.results.failed + (outcome.failed ? 1 : 0),
|
||||
};
|
||||
let status = results.failed > 0 ? 1 : 0;
|
||||
try {
|
||||
if (outcome.failed) console.error(failureText(outcome.error));
|
||||
console.log('\n' + '='.repeat(40));
|
||||
printResults(results);
|
||||
console.log('='.repeat(40));
|
||||
} catch {
|
||||
status = 1;
|
||||
}
|
||||
// Capture the result before a late task can affect process.exitCode. The
|
||||
// deadline loser is observed by execution's rejection handler above.
|
||||
process.exitCode = status;
|
||||
let flushDeadline;
|
||||
const flushed = await Promise.race([
|
||||
Promise.all([process.stdout, process.stderr].map(stream => new Promise((resolve, reject) => {
|
||||
let settled = false;
|
||||
const finish = error => {
|
||||
if (settled) return;
|
||||
settled = true;
|
||||
stream.removeListener('error', onError);
|
||||
stream.removeListener('close', onClose);
|
||||
if (error) reject(error);
|
||||
else resolve();
|
||||
};
|
||||
const onError = error => finish(error || new Error('Test output stream failed'));
|
||||
const onClose = () => finish(new Error('Test output stream closed before flush'));
|
||||
if (stream.destroyed || stream.writableEnded) {
|
||||
onClose();
|
||||
return;
|
||||
}
|
||||
stream.once('error', onError);
|
||||
stream.once('close', onClose);
|
||||
try { stream.write('', error => error ? onError(error) : finish()); }
|
||||
catch (error) { onError(error); }
|
||||
}))).then(() => true, () => false),
|
||||
new Promise(resolve => { flushDeadline = setTimeout(() => resolve(false), flushTimeoutMs); }),
|
||||
]);
|
||||
clearTimeout(flushDeadline);
|
||||
process.exit(flushed ? status : 1);
|
||||
}
|
||||
|
||||
// Compile the exact trusted module privately; this is not a security sandbox.
|
||||
// Relative dependencies retain normal resolution, without rewriting source or
|
||||
// changing module loaders, shared exports, or require.cache.
|
||||
@@ -928,19 +991,15 @@ async function integrationCleanupTests(test) {
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
async function main(suite = createTestRunner()) {
|
||||
console.log('\n=== Testing plan-canvas server ===\n');
|
||||
|
||||
const suite = createTestRunner();
|
||||
const { test } = suite;
|
||||
await artifactSecurityTests(test);
|
||||
await artifactRaceTests(test);
|
||||
await fixtureIsolationTests(test);
|
||||
await integrationCleanupTests(test);
|
||||
if (process.argv.includes('--artifact-security-only')) {
|
||||
printResults(suite.results);
|
||||
return;
|
||||
}
|
||||
if (process.argv.includes('--artifact-security-only')) return;
|
||||
|
||||
await withResourceScope(async resources => {
|
||||
const integrationTest = (name, callback) => test(name, () => withResourceScope(owned => callback({
|
||||
@@ -1350,16 +1409,7 @@ async function main() {
|
||||
});
|
||||
});
|
||||
|
||||
console.log('\n' + '='.repeat(40));
|
||||
printResults(suite.results);
|
||||
console.log('='.repeat(40));
|
||||
}
|
||||
|
||||
// Stay nonzero if setup or cleanup stalls without a live handle or summary.
|
||||
process.exitCode = 1;
|
||||
main().catch(err => {
|
||||
console.error(failureText(err));
|
||||
console.log('Passed: 0');
|
||||
console.log('Failed: 1');
|
||||
process.exitCode = 1;
|
||||
});
|
||||
const suite = createTestRunner();
|
||||
runTestProcess(() => main(suite), suite);
|
||||
|
||||
Reference in New Issue
Block a user