import { HttpResponse, http } from "msw"; import { describe, expect, it, vi } from "vitest"; import { server } from "@/test/server"; import { apiClient, configureAuthClient } from "./client"; describe("apiClient authentication", () => { it("refreshes once and retries an unauthorized protected request", async () => { let token = "expired"; const refresh = vi.fn(async () => { token = "fresh"; return true; }); const authorization: string[] = []; server.use( http.get("*/api/v1/brokerage-accounts", ({ request }) => { authorization.push(request.headers.get("Authorization") ?? ""); return authorization.length === 1 ? HttpResponse.json({ code: "UNAUTHORIZED" }, { status: 401 }) : HttpResponse.json([]); }), ); configureAuthClient({ accessToken: () => token, refresh, clear: vi.fn() }); const { data } = await apiClient.GET("/api/v1/brokerage-accounts"); expect(data).toEqual([]); expect(refresh).toHaveBeenCalledOnce(); expect(authorization).toEqual(["Bearer expired", "Bearer fresh"]); }); it("clears the session when refresh cannot restore it", async () => { const clear = vi.fn(); server.use( http.get("*/api/v1/brokerage-accounts", () => HttpResponse.json({ code: "UNAUTHORIZED" }, { status: 401 }), ), ); configureAuthClient({ accessToken: () => "expired", refresh: async () => false, clear, }); await apiClient.GET("/api/v1/brokerage-accounts"); expect(clear).toHaveBeenCalledOnce(); }); it("does not attach Bearer auth to authentication endpoints", async () => { let authorization: string | null = null; server.use( http.post("*/api/v1/auth/refresh", ({ request }) => { authorization = request.headers.get("Authorization"); return HttpResponse.json({ code: "UNAUTHORIZED" }, { status: 401 }); }), ); configureAuthClient({ accessToken: () => "token", refresh: vi.fn(), clear: vi.fn(), }); await apiClient.POST("/api/v1/auth/refresh"); expect(authorization).toBeNull(); }); it("discards a retried request response after another session takes over", async () => { let generation = 0; let calls = 0; let release!: () => void; const pending = new Promise((resolve) => { release = resolve; }); server.use( http.get("*/api/v1/brokerage-accounts", async () => { if (++calls === 1) return HttpResponse.json({ code: "UNAUTHORIZED" }, { status: 401 }); await pending; return HttpResponse.json([{ id: "previous-user-account" }]); }), ); configureAuthClient({ accessToken: () => "synthetic", generation: () => generation, refresh: async () => true, clear: vi.fn(), }); const response = expect( apiClient.GET("/api/v1/brokerage-accounts"), ).rejects.toMatchObject({ name: "AbortError" }); await vi.waitFor(() => expect(calls).toBe(2)); generation += 1; release(); await response; }); });