Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
87b4005
test: run process.on('exit') handlers in `bun test`
cirospaciari Jul 17, 2026
5ec1092
node:test: implement run()
cirospaciari Jul 17, 2026
9c7ab47
node:test: expectFailure, skipped-suite semantics, and run() event fi…
cirospaciari Jul 17, 2026
f4887dd
node:test: read serialized error fields once when rebuilding the event
cirospaciari Jul 17, 2026
a7e40f2
node:test: address review comments
cirospaciari Jul 17, 2026
f9677c8
node:test: lazy TestsStream, reuse module-scope assert
cirospaciari Jul 17, 2026
fddbcb9
test: don't assert empty stdout for the exit-handler run (Windows ban…
cirospaciari Jul 18, 2026
a894d04
test: drain the event loop for node tests; fix addAbortListener and S…
cirospaciari Jul 18, 2026
6117530
Merge branch 'main' into claude/node-test-run-api
cirospaciari Jul 18, 2026
ff4a202
addAbortListener: keep listenerCount observable; clippy + run() gates
robobun Jul 18, 2026
38b1ccd
events: null-prototype disposable from addAbortListener
cirospaciari Jul 18, 2026
b767c77
test: rename suite-body markers to avoid the diff-hygiene grep
robobun Jul 18, 2026
f544f17
node:test: tag expectFailure validation mismatch as testCodeFailure
robobun Jul 18, 2026
e2eeadb
Merge branch 'main' into claude/node-test-run-api
cirospaciari Jul 18, 2026
385f7a5
test: cover an expectFailure error that misses the validator
cirospaciari Jul 18, 2026
4991483
node:test run(): apply serialized error.name on the parent side; net:…
robobun Jul 18, 2026
2de9193
addAbortListener: remove the algorithm before the counted listener on…
robobun Jul 18, 2026
e3f4998
review: drain flag for run() children, null-proto descriptors, drop d…
robobun Jul 18, 2026
b9b8cfd
node:test run(): validate concurrency/timeout/signal; events: cache t…
robobun Jul 18, 2026
e9db61b
ci: retrigger
robobun Jul 18, 2026
09f31c6
node:test run(): null-proto at the fileNode spread sites
robobun Jul 18, 2026
0a0162a
test_command: use on_before_exit() for the opt-in drain; node:test: i…
robobun Jul 18, 2026
4854fdf
node:test run(): use makeTestFailure for the file-died error; Error.i…
robobun Jul 18, 2026
16f3806
node:test: match node's pass() expectFailure check; null-proto republ…
robobun Jul 18, 2026
ea84bd7
Merge branch 'main' into claude/node-test-run-api
cirospaciari Jul 19, 2026
c9fc677
node:test: check child's own options.todo in scheduleSubtest rollup
robobun Jul 19, 2026
a1d1194
node:test: track own-todo (options.todo or test.todo()) for the subte…
robobun Jul 19, 2026
c0c078b
Merge branch 'main' into claude/node-test-run-api
cirospaciari Jul 22, 2026
d10faf6
Merge remote-tracking branch 'origin/main' into claude/node-test-run-api
robobun Jul 22, 2026
cc53012
Merge remote-tracking branch 'origin/main' into claude/node-test-run-api
robobun Jul 23, 2026
0889d36
Merge branch 'main' into claude/node-test-run-api
cirospaciari Jul 23, 2026
853214d
node:test: implement the deferred run() fidelity and option-gating fo…
robobun Jul 23, 2026
2136128
[autofix.ci] apply automated fixes
autofix-ci[bot] Jul 23, 2026
7dbabb7
node:test: fix oxlint destructure warnings, ownTodo rollup for inline…
robobun Jul 23, 2026
2df3702
Merge branch 'claude/node-test-run-api' of https://github.com/oven-sh…
robobun Jul 23, 2026
8e70112
node:test: stamp todoFlag on describe.todo suites at collection time
robobun Jul 23, 2026
833c313
node:test: skip-wins for .todo + {skip}, preserve --todo verdict unde…
robobun Jul 23, 2026
a91a1dc
node:test run(): stop spawning after abort, defuse stderr drain, and …
robobun Jul 23, 2026
eac9dab
node:test run(): gate concurrency, emit file-level pass for zero-test…
robobun Jul 23, 2026
e809f20
node:test run(): carry todo on inline-suite completion and count the …
robobun Jul 24, 2026
a49f3a2
node:test: chain execution-phase skip directive emits onto subtestChain
robobun Jul 24, 2026
22d1844
node:test run(): drop dead subtestsFailed branch in runOneFile
robobun Jul 24, 2026
e8b6064
Merge branch 'main' into claude/node-test-run-api
dylan-conway Jul 24, 2026
9ab4918
node:test run(): report remaining files as cancelledByParent on abort…
robobun Jul 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions scripts/runner.node.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -759,11 +759,36 @@ async function runTests() {
runWithBunTest ||= title === "test/js/node/test/parallel/test-fs-append-file-flush.js";
runWithBunTest ||= title === "test/js/node/test/parallel/test-fs-write-file-flush.js";
runWithBunTest ||= title === "test/js/node/test/parallel/test-fs-write-stream-flush.js";
// A file that only drives node:test's run() is the parent of the run,
// not a test file: Node executes it as a plain script, and under
// `bun test` a file registering no tests of its own exits before its
// run() finishes. Files that also register tests at the top level (as
// opposed to inside a NODE_TEST_CONTEXT child branch) still need
// `bun test`. run() spawns its own children with `bun test`.
const importsRun =
/\brun\b[^\n]*=\s*require\(['"]node:test['"]\)/.test(testContent) ||
/import\s*{[^}]*\brun\b[^}]*}\s*from\s*['"]node:test['"]/.test(testContent);
// Registrations behind a NODE_TEST_CONTEXT guard belong to the child
// run() spawns, not to this process; an unindented one is this file's
// own and must keep `bun test`, or it silently never runs and the file
// "passes" having tested nothing. Requiring the guard as well keeps a
// file whose only registrations are indented for some other reason
// (inside an `if`, an IIFE) on `bun test`, where the worst case is a
// real run rather than a vacuous pass.
const registersAtColumnZero = /^(?:test|it|describe|suite)\s*[.(]/m.test(testContent);
const registersTests = /(?:^|[^.\w])(?:test|it|describe|suite)\s*[.(]/m.test(testContent);
const guardsOnTestContext = testContent.includes("NODE_TEST_CONTEXT");
const isRunDriver = importsRun && !registersAtColumnZero && (!registersTests || guardsOnTestContext);
// The needs-test filename opt-in wins over this heuristic.
if (isRunDriver && !title.includes("needs-test")) runWithBunTest = false;
Comment thread
coderabbitai[bot] marked this conversation as resolved.
const subcommand = runWithBunTest ? "test" : "run";
const env = {
FORCE_COLOR: "0",
NO_COLOR: "1",
BUN_DEBUG_QUIET_LOGS: "1",
// Node parity: a node test process exits only when its event loop
// drains, and common.mustCall() verifies counts in 'exit' handlers.
BUN_TEST_DRAIN_EVENT_LOOP: "1",
};
if (!isWindows && title.includes("/sequential/")) {
// Sequential node tests share common.PORT (12346); a cluster worker
Expand Down
4 changes: 4 additions & 0 deletions src/bun_core/env_var.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,10 @@ platform_specific_new!(pub C_INCLUDE_PATH: string, posix = "C_INCLUDE_PATH", win
// Standard C compiler environment variable for library paths (colon-separated).
// Used by bun:ffi's TinyCC integration for systems like NixOS.
platform_specific_new!(pub LIBRARY_PATH: string, posix = "LIBRARY_PATH", windows = None, {});
// Drain the event loop after a file's tests finish so node-style
// `process.on('exit')` checks (e.g. common.mustCall) see completed async work.
// Opt-in for the vendored node:test suite and run() children.
new!(pub BUN_TEST_DRAIN_EVENT_LOOP: boolean, "BUN_TEST_DRAIN_EVENT_LOOP", { default: false });
new!(pub BUN_TMPDIR: string, "BUN_TMPDIR", {});
new!(pub BUN_TRACY_PATH: string, "BUN_TRACY_PATH", {});
new!(pub BUN_WATCHER_TRACE: string, "BUN_WATCHER_TRACE", {});
Expand Down
4 changes: 4 additions & 0 deletions src/js/node/net.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3551,6 +3551,10 @@ Server.prototype.close = function close(callback) {
};

Server.prototype[Symbol.asyncDispose] = function () {
// Node resolves immediately when the server is not listening (lib/net.js
// SymbolAsyncDispose); without the guard a second dispose rejects with
// ERR_SERVER_NOT_RUNNING and re-emits 'close'.
if (!this._handle) return Promise.$resolve();
Comment thread
robobun marked this conversation as resolved.
const { resolve, reject, promise } = Promise.withResolvers();
this.close(function (err, ...args) {
if (err) reject(err);
Expand Down
Loading
Loading