Compare commits

...

2 Commits

Author SHA1 Message Date
Claude Bot
e0070ae192 fix: forward options in TestContext.test() and adjust test timeout
Address review feedback:
- Forward options (including timeout) to bun:test in all
  TestContext.test() branches (only, todo, skip, default)
- Reduce outer test timeout from 30s to 15s with explanatory comment

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-25 16:24:55 +00:00
Claude Bot
e1140dd834 fix(test): use no-timeout default for node:test to match Node.js semantics
Node.js's `node:test` defaults to `Infinity` timeout (tests never time
out), but Bun was applying its own 5000ms default. This caused async
tests taking longer than 5s to fail unexpectedly.

Two changes:
- Set timeout=0 (no timeout) as the default for node:test when the user
  hasn't specified one, matching Node.js behavior.
- Replace the done-callback wrapper with an async function to avoid
  bun:test misinterpreting the wrapper's `done` parameter as a
  done-callback style test, which caused misleading error messages.

Closes #27422

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-25 16:12:51 +00:00
3 changed files with 47 additions and 24 deletions

View File

@@ -149,13 +149,13 @@ class TestContext {
const { test } = bunTest();
if (options.only) {
test.only(name, fn);
test.only(name, fn, options);
} else if (options.todo) {
test.todo(name, fn);
test.todo(name, fn, options);
} else if (options.skip) {
test.skip(name, fn);
test.skip(name, fn, options);
} else {
test(name, fn);
test(name, fn, options);
}
}
@@ -304,32 +304,25 @@ function createTest(arg0: unknown, arg1: unknown, arg2: unknown) {
checkNotInsideTest(ctx, "test");
const context = new TestContext(true, name, Bun.main, ctx);
const runTest = (done: (error?: unknown) => void) => {
// Return an async function instead of a done-callback style function.
// Using (done) => {} would cause bun:test to interpret the function as
// a done-callback test (because callback.length >= 1), leading to
// misleading "done callback" error messages on timeout.
const runTest = async () => {
const originalContext = ctx;
ctx = context;
const endTest = (error?: unknown) => {
try {
done(error);
} finally {
ctx = originalContext;
}
};
let result: unknown;
try {
result = fn(context);
} catch (error) {
endTest(error);
return;
}
if (result instanceof Promise) {
(result as Promise<unknown>).then(() => endTest()).catch(error => endTest(error));
} else {
endTest();
await fn(context);
} finally {
ctx = originalContext;
}
};
return { name, options, fn: runTest };
// Node.js node:test defaults to Infinity timeout (no timeout).
// In bun:test, timeout=0 means "no timeout", so use that as default.
const testOptions = { ...options, timeout: options.timeout ?? 0 };
return { name, options: testOptions, fn: runTest };
}
function createDescribe(arg0: unknown, arg1: unknown, arg2: unknown) {

View File

@@ -0,0 +1,7 @@
import { it } from "node:test";
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
it("async test exceeding default bun timeout", async () => {
await sleep(7000);
});

View File

@@ -0,0 +1,23 @@
import { expect, test } from "bun:test";
import { bunEnv, bunExe } from "harness";
test("node:test async tests should not time out by default", async () => {
await using proc = Bun.spawn({
cmd: [bunExe(), "test", "--timeout", "5000", import.meta.dir + "/27422-fixture.test.mjs"],
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
});
const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);
const output = stdout + stderr;
// The test should pass because node:test defaults to no timeout,
// even though bun:test's default is 5000ms.
expect(output).toContain("1 pass");
expect(output).toContain("0 fail");
// Should not contain the misleading "done callback" error message
expect(output).not.toContain("done callback");
expect(exitCode).toBe(0);
// The spawned test sleeps for 7s, so this outer bun:test needs a longer timeout.
}, 15_000);