also looking at this
test: retain execution lifetime failure observations
#62
10 files
+174
-64
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -120,7 +120,8 @@ response. The existing workspace browser scenarios remain required regressions. | ||
| 120 | 120 | The native lifetime fixture emits bounded phase observations around setup, |
| 121 | 121 | authority reads, retention waits and cleanup. Its parent records the observed |
| 122 | 122 | exit code and signal separately from whether its own watchdog fired or cleanup |
| 123 | requested a kill. Elapsed time includes process work and output handling, not | |
| 123 | requested a kill. The settled `exited` status is distinct from the raw | |
| 124 | `exitCode` property, which can remain null after a signal. Elapsed time includes process work and output handling, not | |
| 124 | 125 | just a native check. Failed cases retain their private temporary directory, |
| 125 | 126 | stdout, stderr and lifecycle receipt when storage permits. A fixed warning |
| 126 | 127 | identifies each unavailable evidence file if its write fails; this does not |
| @@ -8,6 +8,7 @@ for (const mode of ['completed', 'canceled', 'quarantine']) test(`native executi | ||
| 8 | 8 | if (evidence) await mkdir(resolve(evidence), { recursive: true, mode: 0o700 }) |
| 9 | 9 | const root = await mkdtemp(join(evidence ? resolve(evidence) : tmpdir(), 'drone-incident-access-execution-lifetime-')) |
| 10 | 10 | let child: ReturnType<typeof Bun.spawn> | undefined |
| 11 | let settledExitCode: number | null = null | |
| 11 | 12 | const began = performance.now() |
| 12 | 13 | let deadlineFired = false, cleanupKill = false, completed = false |
| 13 | 14 | let stdoutWritten = false, stderrWritten = false |
| @@ -18,15 +19,16 @@ for (const mode of ['completed', 'canceled', 'quarantine']) test(`native executi | ||
| 18 | 19 | try { |
| 19 | 20 | const config = join(root, 'bunfig.toml'); await writeFile(config, 'preload=[]\n[install]\nauto="disable"\n') |
| 20 | 21 | const running = child = Bun.spawn([process.execPath, '--no-install', `--config=${config}`, '--no-env-file', `${import.meta.dir}/../fixtures/public-safety-access/execution-lifetime.ts`, mode], { cwd: root, env: { APP_ENV: 'test', APP_DEBUG: 'false', NODE_ENV: 'test', DB_CONNECTION: 'sqlite', DB_DATABASE_PATH: join(root, 'access.sqlite') }, stdin: 'ignore', stdout: 'pipe', stderr: 'pipe' }) |
| 22 | const exited = running.exited.then(code => { settledExitCode = code; return code }) | |
| 21 | 23 | const timeout = setTimeout(() => { |
| 22 | if (running.exitCode !== null) return | |
| 24 | if (settledExitCode !== null) return | |
| 23 | 25 | deadlineFired = true |
| 24 | 26 | running.kill('SIGKILL') |
| 25 | 27 | console.error('LIFETIME_FIXTURE_DEADLINE', JSON.stringify({ mode, pid: running.pid, timeoutMs: 90000, elapsedMs: performance.now() - began, requestedSignal: 'SIGKILL' })) |
| 26 | 28 | }, 90000) |
| 27 | 29 | try { |
| 28 | 30 | console.log(JSON.stringify({ oracle: 'lifetime-fixture-start', mode, pid: running.pid, timeoutMs: 90000 })) |
| 29 | const [code, stdout, stderr] = await Promise.all([running.exited, new Response(running.stdout).text(), new Response(running.stderr).text()]) | |
| 31 | const [code, stdout, stderr] = await Promise.all([exited, new Response(running.stdout).text(), new Response(running.stderr).text()]) | |
| 30 | 32 | // eslint-disable-next-line no-console |
| 31 | 33 | for (const line of stdout.split('\n')) if (line.startsWith('{"oracle":')) console.log(line) |
| 32 | 34 | stdoutWritten = await retain('fixture.stdout', stdout) |
| @@ -35,11 +37,14 @@ for (const mode of ['completed', 'canceled', 'quarantine']) test(`native executi | ||
| 35 | 37 | expect(stdout).toContain(`PASS native execution lifetime ${mode}`) |
| 36 | 38 | completed = true |
| 37 | 39 | } |
| 38 | finally { clearTimeout(timeout); if (running.exitCode === null) { cleanupKill = true; running.kill('SIGKILL') }; await running.exited } | |
| 40 | finally { clearTimeout(timeout); if (settledExitCode === null) { cleanupKill = true; running.kill('SIGKILL') }; settledExitCode = await exited } | |
| 39 | 41 | } |
| 40 | 42 | finally { |
| 41 | if (child?.exitCode === null) { cleanupKill = true; child.kill('SIGKILL'); await child.exited } | |
| 42 | const lifecycle = { mode, pid: child?.pid ?? null, exitCode: child?.exitCode ?? null, signal: child?.signalCode ?? null, timeoutMs: 90000, deadlineFired, cleanupKill, elapsedMs: performance.now() - began, stdoutWritten, stderrWritten, completed } | |
| 43 | if (child && settledExitCode === null) { | |
| 44 | if (child.exitCode === null && !child.signalCode) { cleanupKill = true; child.kill('SIGKILL') } | |
| 45 | settledExitCode = await child.exited | |
| 46 | } | |
| 47 | const lifecycle = { mode, pid: child?.pid ?? null, exitCode: settledExitCode, exitProperty: child?.exitCode ?? null, signal: child?.signalCode ?? null, timeoutMs: 90000, deadlineFired, cleanupKill, elapsedMs: performance.now() - began, stdoutWritten, stderrWritten, completed } | |
| 43 | 48 | console.log(JSON.stringify({ oracle: 'lifetime-fixture-exit', ...lifecycle })) |
| 44 | 49 | await retain('lifecycle.json', JSON.stringify(lifecycle, null, 2)) |
| 45 | 50 | if (completed) await rm(root, { recursive: true, force: true }) |