also looking at this
test: retain execution HTTP lifecycle evidence
#95
3 files
+150
-58
| @@ -1,25 +1,63 @@ | ||
| 1 | 1 | import { expect, test } from 'bun:test' |
| 2 | import { mkdtemp, rm, writeFile } from 'node:fs/promises' | |
| 2 | import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises' | |
| 3 | 3 | import { tmpdir } from 'node:os' |
| 4 | import { join } from 'node:path' | |
| 4 | import { join, resolve } from 'node:path' | |
| 5 | 5 | |
| 6 | 6 | for (const nativeRoutes of [false, true]) for (const scenario of ['limits', 'preflight', 'middleware', 'chunk', 'admission']) test(`native execution HTTP ${scenario} nativeRoutes=${nativeRoutes}`, async () => { |
| 7 | const directory = await mkdtemp(join(tmpdir(), 'drone-incident-access-execution-http-limits-')) | |
| 7 | const evidence = process.env.EXECUTION_HTTP_EVIDENCE | |
| 8 | if (evidence) await mkdir(resolve(evidence), { recursive: true, mode: 0o700 }) | |
| 9 | const directory = await mkdtemp(join(evidence ? resolve(evidence) : tmpdir(), 'drone-incident-access-execution-http-limits-')) | |
| 8 | 10 | let child: ReturnType<typeof Bun.spawn> | undefined |
| 11 | let settledExitCode: number | null = null, settledAtMs: number | null = null | |
| 12 | let watchdogFired = false, watchdogKill = false, cleanupKill = false, completed = false | |
| 13 | let stdoutWritten = false, stderrWritten = false | |
| 14 | const began = performance.now() | |
| 15 | const report = (message: string, error = false) => { | |
| 16 | try { if (error) console.error(message); else console.log(message) } | |
| 17 | catch {} // Evidence delivery cannot replace a settled child verdict. | |
| 18 | } | |
| 19 | const retain = async (artifact: 'fixture.stdout' | 'fixture.stderr' | 'lifecycle.json', contents: string) => { | |
| 20 | try { await writeFile(join(directory, artifact), contents, { mode: 0o600, flag: 'wx' }); return true } | |
| 21 | catch { | |
| 22 | report(`HTTP_FIXTURE_EVIDENCE_UNAVAILABLE ${JSON.stringify({ scenario, nativeRoutes, artifact })}`, true) | |
| 23 | return false | |
| 24 | } | |
| 25 | } | |
| 9 | 26 | try { |
| 10 | 27 | const config = join(directory, 'bunfig.toml'); await writeFile(config, 'preload=[]\n[install]\nauto="disable"\n') |
| 11 | 28 | const running = child = Bun.spawn([process.execPath, '--no-install', `--config=${config}`, '--no-env-file', `${import.meta.dir}/../fixtures/public-safety-access/execution-http-limits.ts`, scenario], { |
| 12 | 29 | cwd: directory, env: { ...process.env, APP_ENV: 'test', NODE_ENV: 'test', DB_CONNECTION: 'sqlite', DB_DATABASE_PATH: join(directory, 'access.sqlite'), INCIDENT_NATIVE_ROUTES: String(nativeRoutes) }, stdout: 'pipe', stderr: 'pipe', |
| 13 | 30 | }) |
| 14 | const timeout = setTimeout(() => { if (running.exitCode === null) running.kill('SIGKILL') }, 35000) | |
| 31 | const exited = running.exited.then(code => { settledExitCode = code; settledAtMs = performance.now() - began; return code }) | |
| 32 | const timeout = setTimeout(() => { | |
| 33 | watchdogFired = true | |
| 34 | if (settledExitCode !== null) return | |
| 35 | watchdogKill = true | |
| 36 | running.kill('SIGKILL') | |
| 37 | report(JSON.stringify({ oracle: 'execution-http-watchdog', scenario, nativeRoutes, pid: running.pid, timeoutMs: 35000, elapsedMs: performance.now() - began, requestedSignal: 'SIGKILL' }), true) | |
| 38 | }, 35000) | |
| 15 | 39 | try { |
| 16 | const [code, stdout, stderr] = await Promise.all([running.exited, new Response(running.stdout).text(), new Response(running.stderr).text()]) | |
| 40 | report(JSON.stringify({ oracle: 'execution-http-fixture-start', scenario, nativeRoutes, pid: running.pid, parentBunVersion: Bun.version, childExecutable: process.execPath, timeoutMs: 35000 })) | |
| 41 | const [code, stdout, stderr] = await Promise.all([exited, new Response(running.stdout).text(), new Response(running.stderr).text()]) | |
| 17 | 42 | // eslint-disable-next-line no-console |
| 18 | for (const line of stdout.split('\n')) if (line.startsWith('{"oracle":')) console.log(line) | |
| 19 | expect(code, `${stdout}\n${stderr}`).toBe(0) | |
| 20 | expect(stdout).toContain(`PASS native execution HTTP ${scenario} nativeRoutes=${nativeRoutes}`) | |
| 43 | for (const line of stdout.split('\n')) if (line.startsWith('{"oracle":')) report(line) | |
| 44 | stdoutWritten = await retain('fixture.stdout', stdout) | |
| 45 | stderrWritten = await retain('fixture.stderr', stderr) | |
| 46 | expect(code, JSON.stringify({ scenario, nativeRoutes, settledExitCode, signal: running.signalCode, watchdogFired, watchdogKill, elapsedMs: performance.now() - began })).toBe(0) | |
| 47 | expect(stdout.includes(`PASS native execution HTTP ${scenario} nativeRoutes=${nativeRoutes}`), 'HTTP fixture completion sentinel is missing; raw output is retained privately').toBe(true) | |
| 48 | completed = true | |
| 49 | } | |
| 50 | finally { clearTimeout(timeout); if (settledExitCode === null) { cleanupKill = true; running.kill('SIGKILL') }; settledExitCode = await exited } | |
| 51 | } | |
| 52 | finally { | |
| 53 | if (child && settledExitCode === null) { | |
| 54 | if (child.exitCode === null && child.signalCode === null) { cleanupKill = true; child.kill('SIGKILL') } | |
| 55 | settledExitCode = await child.exited | |
| 21 | 56 | } |
| 22 | finally { if (running.exitCode === null) running.kill('SIGKILL'); await running.exited; clearTimeout(timeout) } | |
| 57 | const lifecycle = { scenario, nativeRoutes, pid: child?.pid ?? null, settledExitCode, exitProperty: child?.exitCode ?? null, signal: child?.signalCode ?? null, timeoutMs: 35000, watchdogFired, watchdogKill, cleanupKill, settledAtMs, elapsedMs: performance.now() - began, stdoutWritten, stderrWritten, completed } | |
| 58 | report(JSON.stringify({ oracle: 'execution-http-fixture-exit', ...lifecycle })) | |
| 59 | const lifecycleWritten = await retain('lifecycle.json', JSON.stringify(lifecycle, null, 2)) | |
| 60 | if (completed && stdoutWritten && stderrWritten && lifecycleWritten) await rm(directory, { recursive: true, force: true }) | |
| 61 | else report(`HTTP_FIXTURE_RETAINED ${directory}`, true) | |
| 23 | 62 | } |
| 24 | finally { if (child?.exitCode === null) { child.kill('SIGKILL'); await child.exited }; await rm(directory, { recursive: true, force: true }) } | |
| 25 | 63 | }, 40000) |