1import { describe, expect, test } from "alchemy-test";
2import { fileURLToPath } from "node:url";
3
4const fixturesDir = fileURLToPath(new URL("./fixtures/", import.meta.url));
5
6describe("Bun adapter fallback cleanup", { tags: ["unit", "local"] }, () => {
7 test(
8 "closes the shared scope when a user afterAll throws",
9 async () => {
10 // bun:test stops the afterAll chain on the first throw, so without
11 // the adapter's teardown guard the microtask-registered fallback
12 // (which closes the shared scope + sidecar) never runs. The fixture's
13 // shared-scope finalizer printing proves the guard closed the scope;
14 // the non-zero exit proves the teardown failure still fails the run.
15 const child = Bun.spawn(
16 ["bun", "test", "./bun-teardown-guard.fixture.ts"],
17 {
18 cwd: fixturesDir,
19 stdout: "pipe",
20 stderr: "pipe",
21 env: { ...process.env, NO_COLOR: "1", ALCHEMY_DEV: "" },
22 },
23 );
24 const [exitCode, stdout, stderr] = await Promise.all([
25 child.exited,
26 new Response(child.stdout).text(),
27 new Response(child.stderr).text(),
28 ]);
29 const output = `${stdout}\n${stderr}`;
30
31 expect(exitCode).not.toBe(0);
32 expect(output).toContain("teardown-assertion-failed");
33 const userHook = output.indexOf("BUN_GUARD:user-afterAll-throws");
34 const finalizer = output.indexOf("BUN_GUARD:shared-scope-finalizer-ran");
35 expect(userHook).toBeGreaterThanOrEqual(0);
36 // The cleanup ran, and ran after the failing user teardown.
37 expect(finalizer).toBeGreaterThan(userHook);
38 },
39 { timeout: 60_000 },
40 );
41});