bun test: exit 1 when a process.on('exit') listener throws
RO
2mo ago+48+48+01234567890123456789-0-0-0123456789
Problem
- When
bun testruns a file'sprocess.on('exit')listeners and one of them throws (or fails anexpectorassert), the error is printed but the run still exits 0.bun runexits 1 for the same listener, and node's test runner reports the file as failed. bun testruns exit listeners only for files that used anode:testAPI, or underBUN_TEST_DRAIN_EVENT_LOOP=1(#38442). The cause is inTestCommand::exec(src/runtime/cli/test_command.rs:2674): it computes the exit code, callsvm.on_exit(), then callsglobal_exit()with the precomputed code. The listener's error goes throughVirtualMachine::uncaught_exception, which only bumpsunhandled_error_counterunderbun test.
Fix
- Read
vm.unhandled_error_counterbeforeon_exit()and again after. If it grew, setexit_handler.exit_code = 1. - This is the one place that owns the test run's exit code. The error is already printed by the existing handler. No summary line changes.
- Verified:
test/cli/test/bun-test.test.ts(two new tests in theprocess.on('exit') listenersblock: anode:testfile and abun:testfile underBUN_TEST_DRAIN_EVENT_LOOP=1). Both exit 0 on main. Also rantest/js/node/test_runner/node-test.test.tsand the rest ofbun-test.test.ts.
Background
ExitHandler::dispatch_on_exit(src/jsc/VirtualMachine.rs:683) emitsexiton the process object. The emitter reports a listener's throw throughBun__reportUnhandledError, which underbun testcounts it and prints it, but does not touch the exit code.- For plain
bun:testfiles the listeners are not dispatched at all (#38442 matched jest and vitest), so this change does not affect them. --parallelworkers exit through their own path insrc/runtime/cli/test/parallel/runner.rswith a fixed code of 0 and report results over IPC. This PR does not change that path.
Notes
Repro on main:
// exit.test.ts
import { test } from "node:test";
import assert from "node:assert";
test("a", () => {
process.on("exit", code => { assert.strictEqual(1, 2); });
});
bun test exit.test.ts prints the AssertionError and exits 0. node --test exit.test.ts reports the file as not ok with exit code 1.
A bun:test file with the same listener never runs it (since #38442), so there is nothing to fail there. Under BUN_TEST_DRAIN_EVENT_LOOP=1 the listener runs and the same fix applies.
no test proof · iteration 0 · platform-specific test(s) that do not run on this machine, deferring to CI, which covers all platforms: test/cli/test/bun-test.test.ts