Skip to content

Commit dc52da8

Browse files
authored
test: stabilize env boot validation (#321)
1 parent 791ae3e commit dc52da8

1 file changed

Lines changed: 15 additions & 9 deletions

File tree

examples/start-env/test/run.mjs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ async function waitForHttp(url, timeoutMs = 30000, init) {
123123
while (Date.now() - start < timeoutMs) {
124124
try {
125125
const res = await fetch(url, init);
126-
if (res.ok || res.status === 404 || res.status === 500) return;
126+
if (res.ok || res.status === 404 || res.status === 500) return res;
127127
} catch {}
128128
await new Promise((r) => setTimeout(r, 250));
129129
}
@@ -439,10 +439,15 @@ async function prodMode() {
439439
child.stderr.on('data', (d) => (log += d));
440440
return { child, getLog: () => log };
441441
};
442-
const stop = (child) => {
442+
const stop = async (child) => {
443+
if (child.exitCode !== null || child.signalCode !== null) return;
444+
const exited = new Promise((resolve) => child.once('exit', resolve));
443445
try {
444446
process.kill(-child.pid, 'SIGTERM');
445-
} catch {}
447+
} catch {
448+
child.kill('SIGTERM');
449+
}
450+
await exited;
446451
};
447452
const origin = `http://localhost:${PREVIEW_PORT}`;
448453

@@ -455,7 +460,7 @@ async function prodMode() {
455460
record('prod', 'preview', 'app SSRs with the client env value', html.includes('EnvApp'));
456461
record('prod', 'preview', 'server secret never in HTML', !html.includes(SECRET));
457462
checkEnvHeaders('prod', 'preview', headers);
458-
stop(server);
463+
await stop(server);
459464

460465
// 2. Rotation without rebuild: the same artifact, a different secret in
461466
// the process environment (real env wins over the .env fold) — the
@@ -465,18 +470,19 @@ async function prodMode() {
465470
await waitForHttp(origin + '/', 30000, { headers: { accept: 'text/html' } });
466471
const rotated = await fetchPage(origin + '/');
467472
record('prod', 'rotation', 'rotated secret visible without a rebuild (runtime env)', rotated.headers.get('x-env-secret-len') === String(ROTATED.length), `len ${rotated.headers.get('x-env-secret-len')}, want ${ROTATED.length}`);
468-
stop(server);
473+
await stop(server);
469474

470475
// 3. Boot validation: the same artifact with an invalid environment (no
471476
// SESSION_SECRET anywhere) must fail at boot with the per-key report.
472477
writeFileSync(ENV_FILE, ENV_CONTENT.replace(/^SESSION_SECRET=.*$/m, ''));
473478
const failing = preview({});
474-
await waitForHttp(origin + '/', 30000, { headers: { accept: 'text/html' } });
475-
const boot = await fetchPage(origin + '/');
476-
const bootReport = boot.html + failing.getLog();
479+
const boot = await waitForHttp(origin + '/', 30000, {
480+
headers: { accept: 'text/html' },
481+
});
482+
const bootReport = (await boot.text().catch(() => '')) + failing.getLog();
477483
record('prod', 'boot', 'invalid runtime env fails boot (no page served)', boot.status !== 200, `status ${boot.status}`);
478484
record('prod', 'boot', 'boot failure carries the per-key report', /server env validation failed at boot/.test(bootReport) && /SESSION_SECRET/.test(bootReport), bootReport.slice(0, 300));
479-
stop(failing.child);
485+
await stop(failing.child);
480486
writeFileSync(ENV_FILE, ENV_CONTENT);
481487
}
482488

0 commit comments

Comments
 (0)