diff --git a/test/unit/draft.test.ts b/test/unit/draft.test.ts index 7f5ab987ef..0eb0da0107 100644 --- a/test/unit/draft.test.ts +++ b/test/unit/draft.test.ts @@ -1215,9 +1215,17 @@ describe("handleDraftStatus — flag-off guard + populated optional columns", () }); }); -describe("processSubmitDraft — fork-readiness retry loop (fake timers)", () => { +describe("processSubmitDraft — fork-readiness retry loop (instant backoff)", () => { it("polls again after the fork is initially absent, then proceeds once it appears (no real sleep)", async () => { - vi.useFakeTimers(); + // The fork-readiness backoff is sleep(3000) = setTimeout(resolve, 3000). Make it INSTANT (fire on a real + // 0ms tick) so the whole flow runs to completion on the real event loop — no 3s wait, and no fake-timer + // pump to race the interleaved real async (WebCrypto token-decrypt + the async D1/fetch mocks). The old + // fake-timer drive intermittently HUNG under CI coverage load (a real macrotask lagged the microtask flush + // the pump relied on, so the scheduled sleep was never fired and the test timed out). + const realSetTimeout = globalThis.setTimeout; + const timerSpy = vi + .spyOn(globalThis, "setTimeout") + .mockImplementation(((cb: (...a: unknown[]) => void, _ms?: number, ...args: unknown[]) => realSetTimeout(cb, 0, ...args)) as unknown as typeof globalThis.setTimeout); try { const env = draftEnv(); const id = await seedQueuedDraftWithToken(env); @@ -1248,24 +1256,16 @@ describe("processSubmitDraft — fork-readiness retry loop (fake timers)", () => return routes(input, init); }); - let settled = false; - const done = processSubmitDraft(env, id).then(() => { - settled = true; - }); - // The loop interleaves real-async D1/fetch work with the one fake setTimeout(sleep 3000). - // Pump fake timers repeatedly (flushing pending real microtasks each pass) until the whole - // flow settles, so the sleep advances the instant it is scheduled — and never runs for real. - for (let i = 0; i < 50 && !settled; i += 1) { - await vi.advanceTimersByTimeAsync(3000); - } - await done; + // sleep() is now instant, so the flow runs straight through (probe 404 → instant backoff → probe 200 → + // open the PR). Just await it — no pump loop, no fake-timer race. + await processSubmitDraft(env, id); fetchSpy.mockRestore(); expect(forkProbe).toBe(2); // probed once (absent), slept, probed again (present) const row = await env.DB.prepare("SELECT status, pull_request_number FROM submission_drafts WHERE id = ?").bind(id).first<{ status: string; pull_request_number: number }>(); expect(row).toMatchObject({ status: "pr_open", pull_request_number: 808 }); } finally { - vi.useRealTimers(); + timerSpy.mockRestore(); } }); });