import { act } from "react"; import { renderHook, waitFor } from "@testing-library/react"; import type { ReactNode } from "react"; import { AuthProvider, resetAuthProbeForTests, useAuth } from "@/auth/store"; const STORAGE_KEY = "nxdns_auth_required"; function wrapper({ children }: { children: ReactNode }) { return {children}; } function jsonResponse(payload: unknown, status = 200, headers: Record = {}): Response { return new Response(JSON.stringify(payload), { status, headers: { "content-type": "application/json", ...headers }, }); } beforeEach(() => { sessionStorage.clear(); resetAuthProbeForTests(); }); afterEach(() => { vi.unstubAllGlobals(); }); test("mounting with nothing stored probes and settles authRequired true on 401", async () => { const fetchMock = vi.fn(async (input: RequestInfo | URL) => { if (String(input) === "/api/auth/login") return jsonResponse({ error: "password required" }, 401); return jsonResponse({ error: "not stubbed" }, 404); }); vi.stubGlobal("fetch", fetchMock); const { result } = renderHook(() => useAuth(), { wrapper }); expect(result.current.authRequired).toBeNull(); await waitFor(() => expect(result.current.authRequired).toBe(true)); expect(sessionStorage.getItem(STORAGE_KEY)).toBe("true"); }); test("mounting with nothing stored probes and settles authRequired false when auth is off", async () => { vi.stubGlobal( "fetch", vi.fn(async () => jsonResponse({ authenticated: true, auth_required: false })), ); const { result } = renderHook(() => useAuth(), { wrapper }); await waitFor(() => expect(result.current.authRequired).toBe(false)); expect(sessionStorage.getItem(STORAGE_KEY)).toBe("false"); }); test("a stored value is the fast path: no probe fires on mount", async () => { sessionStorage.setItem(STORAGE_KEY, "true"); const fetchMock = vi.fn(); vi.stubGlobal("fetch", fetchMock); const { result } = renderHook(() => useAuth(), { wrapper }); expect(result.current.authRequired).toBe(true); await act(async () => {}); expect(fetchMock).not.toHaveBeenCalled(); }); test("logout swallows a 401 from an already-dead session", async () => { sessionStorage.setItem(STORAGE_KEY, "true"); vi.stubGlobal( "fetch", vi.fn(async () => jsonResponse({ error: "unauthorized" }, 401)), ); const { result } = renderHook(() => useAuth(), { wrapper }); await expect(result.current.logout()).resolves.toBeUndefined(); }); test("logout rethrows non-401 errors such as 429", async () => { sessionStorage.setItem(STORAGE_KEY, "true"); vi.stubGlobal( "fetch", vi.fn(async () => jsonResponse({ error: "rate limited" }, 429, { "retry-after": "7" })), ); const { result } = renderHook(() => useAuth(), { wrapper }); await expect(result.current.logout()).rejects.toMatchObject({ name: "ApiError", status: 429, retryAfter: 7, }); });