Files
TRIMSP/tests/runtime-gate.test.js
salman 0c8e8f2e03 Add runtime-gate test and WSL2 remote smoke-test checklist
- tests/runtime-gate.test.js lifts the shipped ensureRuntimeGateReady() and
  checks its behaviour against stub gates, and statically asserts that
  startSequence() awaits the gate before launching the engine, so a refactor
  cannot silently drop the first-run import wait.
- packaging/wsl2/README.md documents the manual remote-session (SSH/RDP)
  verification, since the engine path is a console process that needs a real
  Windows host and cannot be covered by an automated test.
2026-10-20 09:37:42 +02:00

104 lines
3.6 KiB
JavaScript

// Checks the shared scan pipeline is wired to the shell's runtime gate.
//
// On Windows the bundled WSL2 engine is imported on first run, so a Run must
// wait for the gate before launching the engine. This test guards two things
// without needing a Windows host:
// 1. the behaviour of ensureRuntimeGateReady() against stub gates;
// 2. that startSequence() actually awaits it, so a future refactor cannot
// silently drop the gate and race the import.
//
// Run with: node tests/runtime-gate.test.js
const fs = require('fs');
const path = require('path');
const assert = require('assert');
const libPath = path.join(__dirname, '..', 'TrimSPlib.js');
const src = fs.readFileSync(libPath, 'utf8');
function grab(re) {
const m = src.match(re);
if (!m) {
throw new Error('pattern not found in ' + libPath + ': ' + re);
}
return m[0];
}
// Lift the real ensureRuntimeGateReady() so its shipped code is exercised.
const gateSource = grab(/async function ensureRuntimeGateReady\s*\([^)]*\)\s*\{[\s\S]*?\n\}/m);
let statusCalls = [];
let stubWindow = {};
const factory = new Function('window', 'updateStatusBar',
gateSource + '\nreturn ensureRuntimeGateReady;');
function makeGateFn() {
statusCalls = [];
return factory(stubWindow, function (payload) { statusCalls.push(payload); });
}
let passed = 0;
let failed = 0;
function check(name, ok, detail) {
if (ok) { passed++; } else { failed++; }
console.log((ok ? 'PASS ' : 'FAIL ') + name.padEnd(46) + (detail || ''));
}
async function main() {
// 1. No gate installed (web/standalone) -> proceed, no status write.
stubWindow = {};
let gate = makeGateFn();
let result = await gate('Run');
check('no gate: proceeds', result === true && statusCalls.length === 0);
// 2. Gate resolves true -> proceed.
stubWindow = { TrimSPRuntimeGate: { ready: () => Promise.resolve(true) } };
gate = makeGateFn();
result = await gate('Run');
check('gate ready: proceeds', result === true);
// 3. Gate resolves false -> blocked and message surfaced.
stubWindow = {
TrimSPRuntimeGate: {
ready: () => Promise.resolve(false),
message: () => 'Check that WSL2 is installed, then try again.'
}
};
gate = makeGateFn();
result = await gate('Run');
check('gate not ready: blocked', result === false);
check('gate not ready: status shown',
statusCalls.length === 1
&& /runtime is not ready/.test(statusCalls[0].run)
&& /WSL2 is installed/.test(statusCalls[0].run),
statusCalls.length ? '"' + statusCalls[0].run + '"' : 'no status');
// 4. Gate that throws -> treated as not ready, not an unhandled rejection.
stubWindow = {
TrimSPRuntimeGate: {
ready: () => Promise.reject(new Error('boom')),
message: () => 'failed'
}
};
gate = makeGateFn();
result = await gate('Run');
check('gate throwing: blocked', result === false && statusCalls.length === 1);
// 5. startSequence must await the gate before running the engine.
const startSequence = grab(/async function startSequence\s*\([^)]*\)\s*\{[\s\S]*?\n\}/m);
const guardIndex = startSequence.indexOf('ensureRuntimeGateReady');
const launchIndex = startSequence.indexOf('runSimulationSequence');
check('startSequence awaits gate',
guardIndex !== -1,
guardIndex !== -1 ? '' : 'no ensureRuntimeGateReady() call');
check('gate checked before launch',
guardIndex !== -1 && launchIndex !== -1 && guardIndex < launchIndex);
console.log('\n' + passed + ' passed, ' + failed + ' failed');
process.exit(failed ? 1 : 0);
}
main();