Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
54 changes: 54 additions & 0 deletions test/regression/issue/22743.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// https://github.com/oven-sh/bun/issues/22743
//
// Regressed 1.2.20 → 1.2.21: dynamic-importing path X that throws,
// then a *different* error-throwing path Y, then X again — the third
// `await import(X)` hung instead of re-throwing. Same already-settled
// module-registry-entry re-entry as #23139, just with the X/Y/X
// interleave that the original repro hit via two distinct https URLs.
// Fixed in the module-loader rewrite window (#29393 → #30262); this
// test pins the network-free reduction.

import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";

test("re-importing an error-throwing module after a different error-throwing import re-throws instead of hanging", async () => {
using dir = tempDir("issue-22743", {
"bad1.json": `{ "a": nope }`,
"bad2.json": `{ "b": also nope }`,
"entry.ts": `
// Original repro used two https:// URLs (which Bun rejects without
// a network call); two unparseable JSON files give the same
// throws-on-load shape without depending on the https-import error
// message staying stable.
const seq = ["./bad1.json", "./bad2.json", "./bad1.json"];
for (const [i, p] of seq.entries()) {
try {
await import(p);
console.log("import " + (i + 1) + ": resolved (unexpected)");
} catch (e) {
console.log("import " + (i + 1) + ": threw " + (e as Error).name);
}
}
console.log("done");
`,
});

await using proc = Bun.spawn({
cmd: [bunExe(), "entry.ts"],
cwd: String(dir),
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
// Pre-fix the third import never settles; bound the subprocess so the
// assertions report a clear diff instead of the test itself timing out.
timeout: 10_000,
});

const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);

expect(stdout).toBe("import 1: threw SyntaxError\nimport 2: threw SyntaxError\nimport 3: threw SyntaxError\ndone\n");
if (exitCode !== 0) expect(stderr).toBe("");
// null ⇒ exited on its own; non-null ⇒ killed by the spawn timeout (hung).
expect(proc.signalCode).toBeNull();
expect(exitCode).toBe(0);
}, 30_000);
49 changes: 49 additions & 0 deletions test/regression/issue/23139.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// https://github.com/oven-sh/bun/issues/23139
//
// Dynamically importing a file that fails to parse threw on the first
// `await import()`, but the *second* `await import()` of the same path
// hung forever instead of re-throwing — the rejected module-registry
// entry was never re-queried. Fixed somewhere in the WebKit
// module-loader rewrite window (oven-sh/bun#29393 → #30262); reproduces
// on 1.3.13, gone on main with WebKit 88b2f7a2 (i.e. before #30527).
// This test pins it.

import { expect, test } from "bun:test";
import { bunEnv, bunExe, tempDir } from "harness";

test("repeated dynamic import of a file that fails to parse re-throws instead of hanging", async () => {
using dir = tempDir("issue-23139", {
"bad.json": `{ "invalid": json }`,
"entry.ts": `
for (const attempt of [1, 2]) {
try {
await import("./bad.json");
console.log("attempt " + attempt + ": resolved (unexpected)");
} catch (e) {
console.log("attempt " + attempt + ": threw " + (e as Error).name);
}
}
console.log("done");
`,
});

await using proc = Bun.spawn({
cmd: [bunExe(), "entry.ts"],
cwd: String(dir),
env: bunEnv,
stdout: "pipe",
stderr: "pipe",
// Before the fix the second import never settles, so bound the
// subprocess and assert on signalCode rather than letting the test
// itself time out.
timeout: 10_000,
});

const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]);

expect(stdout).toBe("attempt 1: threw SyntaxError\nattempt 2: threw SyntaxError\ndone\n");
if (exitCode !== 0) expect(stderr).toBe("");
// null ⇒ exited on its own; non-null ⇒ killed by the spawn timeout (hung).
expect(proc.signalCode).toBeNull();
expect(exitCode).toBe(0);
}, 30_000);
Loading