/** * Client auth store (auth-store.ts). Focus: the session lifecycle invariants * the auth bootstrap/logout depend on — `clear()` (logout) must also end any * in-flight initialization so the (app) layout can't keep spinning on a stale * `initializing: true` if logout fires before the bootstrap settled, and * `setUser` must always terminate initialization. */ import { beforeEach, describe, expect, it } from "vitest"; import { authStore, hasSession, useAuthStore } from "@/lib/auth-store"; import type { User } from "@/lib/types"; const user = { id: "u1", email: "a@b.c", name: "A" } as unknown as User; describe("auth-store", () => { beforeEach(() => { // Reset to the initial (signed-out, initializing) state. useAuthStore.setState({ user: null, accessToken: null, initializing: true }); }); it("setUser stores the user + token and ends initialization", () => { authStore.setUser(user, "tok-1"); expect(authStore.user?.id).toBe("u1"); expect(authStore.accessToken).toBe("tok-1"); expect(useAuthStore.getState().initializing).toBe(false); expect(hasSession()).toBe(true); }); it("setAccessToken swaps the token without clearing the user", () => { authStore.setUser(user, "tok-1"); authStore.setAccessToken("tok-2"); expect(authStore.accessToken).toBe("tok-2"); expect(authStore.user?.id).toBe("u1"); }); it("clear() resets user + token AND ends initialization (the logout fix)", () => { // Mid-bootstrap: an access token was just stored, user not yet set, // initialization still spinning. authStore.setAccessToken("tok-1"); expect(useAuthStore.getState().initializing).toBe(true); authStore.clear(); expect(authStore.user).toBeNull(); expect(authStore.accessToken).toBeNull(); expect(hasSession()).toBe(false); // The regression guard: clear() must not leave `initializing: true`. expect(useAuthStore.getState().initializing).toBe(false); }); it("clear() is idempotent when already signed out", () => { authStore.clear(); authStore.clear(); expect(authStore.user).toBeNull(); expect(authStore.accessToken).toBeNull(); expect(useAuthStore.getState().initializing).toBe(false); }); });