import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { DEFAULT_TIMEOUT_MS, NON_IDEMPOTENT_WRITE_TIMEOUT_MS, isNetworkError, } from "@/lib/http/fetch-with-timeout"; import { expectTimeoutAt, stubHangingFetch } from "@/lib/http/deadline-test-helper"; import { getCurrentUserRequest, loginRequest, logoutRequest, refreshRequest, ssoCompleteRequest, verifyMfaRequest, } from "./api"; vi.mock("../config/runtime-config", () => ({ getApiUrl: vi.fn().mockResolvedValue("http://api.test"), })); describe("auth api timeouts", () => { beforeEach(() => { vi.useFakeTimers(); stubHangingFetch(); }); afterEach(() => { vi.useRealTimers(); vi.unstubAllGlobals(); }); it.each([ ["login", () => loginRequest("a@b.de", "pw")], ["MFA verify", () => verifyMfaRequest("t", "123456")], ["refresh", () => refreshRequest()], ["SSO complete", () => ssoCompleteRequest()], ["logout", () => logoutRequest()], ])("%s (one-shot / rotating) uses the long write timeout", async (_name, call) => { await expectTimeoutAt(call, NON_IDEMPOTENT_WRITE_TIMEOUT_MS); }); it("plain reads use the default timeout", async () => { await expectTimeoutAt(() => getCurrentUserRequest("tok"), DEFAULT_TIMEOUT_MS); }); }); describe("refreshRequest outcome", () => { afterEach(() => { vi.unstubAllGlobals(); }); function answerWith(status: number, body: unknown = {}) { vi.stubGlobal( "fetch", vi.fn().mockResolvedValue(new Response(JSON.stringify(body), { status })), ); } it("returns the access token when the server accepts the session", async () => { answerWith(200, { accessToken: "token-1" }); await expect(refreshRequest()).resolves.toEqual({ accessToken: "token-1" }); }); it.each([401, 403])("says the session is over for %i", async (status) => { answerWith(status); await expect(refreshRequest()).resolves.toBeNull(); }); it.each([429, 500, 502, 503, 504])( "does not end the session for %i - the server was just unavailable", async (status) => { answerWith(status); const failure = await refreshRequest().catch((error: unknown) => error); expect(failure).toBeInstanceOf(Error); // Treated like a lost connection by everything that retries or queues. expect(isNetworkError(failure)).toBe(true); }, ); });