From 6e9b81effe3d79f1bbeadf40b3e1049fe056263d Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Thu, 13 Aug 2026 00:46:16 +0000 Subject: [PATCH 1/2] bun:test: cover Bun.serve's Date header timer under fake timers Before #37946 the Date header timer was enrolled in jest.useFakeTimers(): accepting one connection under fake timers made jest.getTimerCount() report 1 with no user timers, and jest.runAllTimers() never returned because the timer re-armed itself into the fake heap on every pop while a keep-alive connection was open. #37946 fixed the enrollment; these tests pin the Bun.serve symptom, which its own tests do not exercise. --- .../test/test-timers-date-header-fixture.ts | 20 +++++++++++ test/js/bun/test/test-timers.test.ts | 33 +++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 test/js/bun/test/test-timers-date-header-fixture.ts diff --git a/test/js/bun/test/test-timers-date-header-fixture.ts b/test/js/bun/test/test-timers-date-header-fixture.ts new file mode 100644 index 000000000000..e50c04987676 --- /dev/null +++ b/test/js/bun/test/test-timers-date-header-fixture.ts @@ -0,0 +1,20 @@ +import { jest, test } from "bun:test"; + +test("runAllTimers returns with a keep-alive connection open and no user timers", async () => { + using server = Bun.serve({ port: 0, hostname: "127.0.0.1", fetch: () => new Response("hi") }); + + jest.useFakeTimers(); + try { + // Accepting the connection arms Bun.serve's internal 1s Date header timer, + // and the keep-alive connection makes it re-arm itself every time it fires. + // Before oven-sh/bun#37946 it was enrolled in the fake heap, so runAllTimers() + // popped it, it re-armed at mocked now + 1s, and the drain never ended. + const res = await fetch(`http://127.0.0.1:${server.port}/`); + await res.text(); + + jest.runAllTimers(); + } finally { + jest.useRealTimers(); + } + console.log("RUN_ALL_OK"); +}); diff --git a/test/js/bun/test/test-timers.test.ts b/test/js/bun/test/test-timers.test.ts index 8d40f210634c..dcca4852c649 100644 --- a/test/js/bun/test/test-timers.test.ts +++ b/test/js/bun/test/test-timers.test.ts @@ -114,3 +114,36 @@ test("real timer heap is ticked against the real clock under useFakeTimers", asy expect(proc.signalCode).toBeNull(); expect(exitCode).toBe(0); }); + +test("Bun.serve's Date header timer is not enrolled in fake timers", async () => { + using server = Bun.serve({ port: 0, hostname: "127.0.0.1", fetch: () => new Response("hi") }); + + jest.useFakeTimers(); + try { + // Accepting the connection arms the internal 1s Date header timer. + const res = await fetch(`http://127.0.0.1:${server.port}/`); + await res.text(); + + expect(jest.getTimerCount()).toBe(0); + } finally { + jest.useRealTimers(); + } +}); + +test("runAllTimers returns while Bun.serve holds a keep-alive connection", async () => { + await using proc = Bun.spawn({ + cmd: [bunExe(), "test", path.join(import.meta.dir, "test-timers-date-header-fixture.ts")], + env: bunEnv, + stdout: "pipe", + stderr: "pipe", + // On a regressed build runAllTimers() never returns (the Date header timer + // re-arms itself into the fake heap on every pop), so bound the child. + timeout: 20_000, + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + if (exitCode !== 0) console.error(stderr); + expect(stdout).toContain("RUN_ALL_OK"); + // null => exited on its own; non-null => killed by the spawn timeout (spun). + expect(proc.signalCode).toBeNull(); + expect(exitCode).toBe(0); +}); From 547d939e64257ebfc2f2293c067938611d3347c6 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Thu, 13 Aug 2026 00:53:44 +0000 Subject: [PATCH 2/2] ci: retrigger