Bun 1.4.3 fake timers: test a Sume poll loop with request timeouts
Bun 1.4.3 fixes advanceTimersByTime spinning with AbortSignal.timeout. Test a Sume job poll loop that honors next_poll_after_seconds without real waits.

Yes, you can unit-test a Sume status-poll loop in bun test with fake timers even when each request carries AbortSignal.timeout(). Bun v1.4.3, released October 10, 2026, fixes advanceTimersByTime() and runOnlyPendingTimers() never returning when an abort listener armed a new AbortSignal.timeout(0). That is a narrower case than a plain per-request timeout, so confirm your own loop under fake timers.
The test below feeds the loop three fake status replies and moves the clock instead of sleeping, so it finishes in milliseconds.
What changed in Bun 1.4.3
The Bun v1.4.3 release notes (read 2026-10-10) list a timers fix: with jest.useFakeTimers(), the two calls no longer hang when an abort listener arms a new AbortSignal.timeout(0). The same notes list an unrelated bun test fix, expect.not.any() verdicts, that you may also hit in poll-loop assertions.
| Area | Change in v1.4.3 |
|---|---|
| Fake timers | advanceTimersByTime() and runOnlyPendingTimers() no longer hang when an abort listener arms AbortSignal.timeout(0) |
| Async matchers | expect.not.any(), expect.resolvesTo.any() and expect.rejectsTo.any() give the right verdict for primitives |
| Parallel safety | With bun test --parallel, a crashed worker no longer leaves processes its test spawned |
The loop under test
Sume's jobs docs say to poll GET /v1/jobs/:id/status until terminal is true, and to obey next_poll_after_seconds when it is present. The function below does that, with a 15-second abort on each request so a hung read cannot stall the loop. It takes fetch as a parameter so a test can replace it.
export async function pollJob(id: string, key: string, fetchFn = fetch) {
for (let attempt = 0; attempt < 30; attempt++) {
const res = await fetchFn(`https://api.sume.com/v1/jobs/${id}/status`, {
headers: { "x-api-key": key },
signal: AbortSignal.timeout(15_000),
});
const s = await res.json();
if (s.terminal) return s;
await new Promise((r) => setTimeout(r, (s.next_poll_after_seconds ?? 2) * 1000));
}
throw new Error("poll deadline");
}The test
The fake fetch returns two non-terminal replies that ask for a 5-second wait, then a terminal one. The test yields once so the fake response resolves, then moves the clock by 5 seconds, twice. Nothing waits in real time.
import { test, expect, jest } from "bun:test";
import { pollJob } from "./poll";
test("polls until terminal without real waiting", async () => {
jest.useFakeTimers();
const replies = [
{ terminal: false, next_poll_after_seconds: 5 },
{ terminal: false, next_poll_after_seconds: 5 },
{ terminal: true, sume_status: "completed" },
];
const fake = (async () => Response.json(replies.shift())) as unknown as typeof fetch;
const done = pollJob("job_1", "k", fake);
for (let i = 0; i < 2; i++) {
await new Promise((r) => setImmediate(r)); // let the fake fetch resolve
jest.advanceTimersByTime(5_000);
}
expect((await done).sume_status).toBe("completed");
jest.useRealTimers();
});What this test does not prove
A green test shows your loop logic is right. It does not show that Sume, your network, or your wallet behave. Keep these limits in mind.
- It does not exercise the network. A real 429 or 402 comes back as a Sume error envelope, not as a status payload, so add a case for that in your own wrapper.
- It does not prove billing safety. A poll deadline in your client does not cancel the job; it keeps running and billing, so store the job id before you give up.
- Pin your Bun version in CI. The fix named above is in 1.4.3; on an older Bun, check that the test finishes instead of hanging.
Sources
Related posts
More in Developers
- Bun 1.4.3 fetch Content-Length check: send a Sume JSON body as bytes
Bun 1.4.3 rejects fetch when a declared Content-Length mismatches a stream body. For a Sume submit, skip the header and pass the encoded JSON.
- bun run --check: type errors stop before a Sume job submits
Bun 1.4.3 adds bun check and a --check flag on bun run. Use it so a type error in your Sume script fails before a paid generation request is sent.
- Can you cancel an image generation on Sume? Only before it starts
Cancelling a Sume image job works only before generation work starts. After that the API returns 409 job_generation_already_started and the job runs and bills.
- Know the day Sume lists a new video model: a catalog check script
Vendors launch video models weekly. A short Python script reads GET /v1/videos/models and flags when an id or name appears, with exit codes for cron or CI.
Written by Sume