From 091e2ba4da83f5db673e67714cd31d4cb48cb0d5 Mon Sep 17 00:00:00 2001 From: Xiangzhe Date: Tue, 25 Aug 2026 17:15:16 -0300 Subject: [PATCH] test(ui): unmount before asserting so the auto-sync timer cannot outlive the test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vitest went red on 'synchronizes upstream models only when autoFetchModels is explicitly true' with new URL throwing inside a fetch dispatched from Timeout._onTimeout (useProviderModels.ts:69). It is intermittent: green in the two previous CI runs, green every time in isolation, red only under the ui suite's 20 parallel workers. The hook schedules its auto-sync in a setTimeout whose callback only checks the flag on entry — and that flag stays false while the component is mounted. Both tests asserted first and unmounted last, so under contention the timer escaped the test window, fired after afterEach had already run vi.unstubAllGlobals(), and reached the REAL fetch with a relative URL. Unmounting before the assertions closes the window: cleanup flips , the callback returns early, and the calls already recorded on fetchMock are still there to assert against. No assertion changed. Not a regression from this cycle — the file's last change is fd76271515 (#10603). Fixed rather than tracked because an intermittent red in a blocking job is worse than a permanent one: it teaches people to re-run instead of to look. Refs #10692 --- .../use-provider-models-auto-fetch.test.tsx | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/tests/unit/ui/use-provider-models-auto-fetch.test.tsx b/tests/unit/ui/use-provider-models-auto-fetch.test.tsx index 08e93f6d03..7b16b04b5c 100644 --- a/tests/unit/ui/use-provider-models-auto-fetch.test.tsx +++ b/tests/unit/ui/use-provider-models-auto-fetch.test.tsx @@ -6,9 +6,8 @@ vi.mock("next-intl", () => ({ useTranslations: () => (key: string) => key, })); -const { useProviderModels } = await import( - "@/app/(dashboard)/dashboard/providers/hooks/useProviderModels" -); +const { useProviderModels } = + await import("@/app/(dashboard)/dashboard/providers/hooks/useProviderModels"); function createResponse(body: unknown, ok = true): Response { return { @@ -67,11 +66,21 @@ describe("useProviderModels upstream auto-fetch", () => { const mounted = await renderProviderModels(); await flushQueuedSync(); + // Desmontar ANTES de asserir. O hook agenda a auto-sync num setTimeout e o + // callback so checa o flag `cancelled` na entrada; enquanto o componente + // estiver montado esse flag e false. Se o timer escapar da janela do teste, + // ele dispara depois do afterEach ja ter feito unstubAllGlobals() e cai no + // fetch REAL com uma URL relativa — `new URL` estoura e derruba o arquivo. + // Isso nao acontece com a maquina ociosa, so sob os 20 workers da suite + // cheia, e foi assim que este teste virou vermelho intermitente no CI. + // Desmontar primeiro faz `cancelled` virar true e o callback sair cedo; as + // chamadas ja registradas no fetchMock continuam disponiveis para o assert. + mounted.unmount(); + expect(fetchMock).not.toHaveBeenCalledWith( "/api/providers/connection-1/sync-models?mode=sync", expect.anything() ); - mounted.unmount(); }); it("synchronizes upstream models only when autoFetchModels is explicitly true", async () => { @@ -101,10 +110,11 @@ describe("useProviderModels upstream auto-fetch", () => { const mounted = await renderProviderModels(); await flushQueuedSync(); - expect(fetchMock).toHaveBeenCalledWith( - "/api/providers/connection-1/sync-models?mode=sync", - { method: "POST" } - ); + // Mesmo motivo do teste acima: desmontar fecha a janela do timer vazado. mounted.unmount(); + + expect(fetchMock).toHaveBeenCalledWith("/api/providers/connection-1/sync-models?mode=sync", { + method: "POST", + }); }); });