theo-agent-dashboard

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

auth.test.ts (5829B)


      1 import { describe, it, expect, vi, beforeEach } from "vitest";
      2 import { useAuthStore } from "./auth";
      3 
      4 beforeEach(() => {
      5   useAuthStore.setState({
      6     authenticated: false,
      7     configured: false,
      8     loading: false,
      9     error: null,
     10   });
     11   vi.restoreAllMocks();
     12 });
     13 
     14 describe("auth store", () => {
     15   it("has initial unauthenticated state", () => {
     16     const state = useAuthStore.getState();
     17     expect(state.authenticated).toBe(false);
     18     expect(state.configured).toBe(false);
     19     expect(state.loading).toBe(false);
     20     expect(state.error).toBeNull();
     21   });
     22 
     23   it("login success updates state to authenticated", async () => {
     24     vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
     25       ok: true, json: async () => ({}),
     26     }));
     27     await useAuthStore.getState().login("correct-password");
     28     expect(useAuthStore.getState().authenticated).toBe(true);
     29     expect(useAuthStore.getState().error).toBeNull();
     30     expect(useAuthStore.getState().loading).toBe(false);
     31   });
     32 
     33   it("login sets loading to true during request", async () => {
     34     let resolveFetch!: (v: unknown) => void;
     35     vi.stubGlobal("fetch", vi.fn().mockReturnValue(
     36       new Promise((resolve) => { resolveFetch = resolve; }),
     37     ));
     38     const loginPromise = useAuthStore.getState().login("test");
     39     expect(useAuthStore.getState().loading).toBe(true);
     40     resolveFetch({ ok: true, json: async () => ({}) });
     41     await loginPromise;
     42     expect(useAuthStore.getState().loading).toBe(false);
     43   });
     44 
     45   it("login failure sets error message from Error instance", async () => {
     46     vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("Invalid password")));
     47     await useAuthStore.getState().login("wrong-password");
     48     expect(useAuthStore.getState().authenticated).toBe(false);
     49     expect(useAuthStore.getState().error).toBe("Invalid password");
     50     expect(useAuthStore.getState().loading).toBe(false);
     51   });
     52 
     53   it("login failure sets generic error for non-Error rejection", async () => {
     54     vi.stubGlobal("fetch", vi.fn().mockRejectedValue("string error"));
     55     await useAuthStore.getState().login("wrong-password");
     56     expect(useAuthStore.getState().authenticated).toBe(false);
     57     expect(useAuthStore.getState().error).toBe("Login failed");
     58     expect(useAuthStore.getState().loading).toBe(false);
     59   });
     60 
     61   it("login clears previous error", async () => {
     62     useAuthStore.setState({ error: "old error" });
     63     vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
     64       ok: true, json: async () => ({}),
     65     }));
     66     await useAuthStore.getState().login("test");
     67     expect(useAuthStore.getState().error).toBeNull();
     68   });
     69 
     70   it("logout clears authenticated state", async () => {
     71     useAuthStore.setState({ authenticated: true });
     72     vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
     73       ok: true, json: async () => ({}),
     74     }));
     75     await useAuthStore.getState().logout();
     76     expect(useAuthStore.getState().authenticated).toBe(false);
     77   });
     78 
     79   it("logout sets unauthenticated even on API error", async () => {
     80     useAuthStore.setState({ authenticated: true });
     81     vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("network error")));
     82     // logout has finally block, so it should set authenticated to false
     83     // even if the API call fails
     84     try {
     85       await useAuthStore.getState().logout();
     86     } catch {
     87       // The error may propagate, but finally still runs
     88     }
     89     expect(useAuthStore.getState().authenticated).toBe(false);
     90   });
     91 
     92   it("checkStatus updates configured and authenticated", async () => {
     93     vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
     94       ok: true, json: async () => ({ configured: true, authenticated: false }),
     95     }));
     96     await useAuthStore.getState().checkStatus();
     97     expect(useAuthStore.getState().configured).toBe(true);
     98     expect(useAuthStore.getState().authenticated).toBe(false);
     99     expect(useAuthStore.getState().loading).toBe(false);
    100   });
    101 
    102   it("checkStatus handles configured + authenticated", async () => {
    103     vi.stubGlobal("fetch", vi.fn().mockResolvedValue({
    104       ok: true, json: async () => ({ configured: true, authenticated: true }),
    105     }));
    106     await useAuthStore.getState().checkStatus();
    107     expect(useAuthStore.getState().configured).toBe(true);
    108     expect(useAuthStore.getState().authenticated).toBe(true);
    109   });
    110 
    111   it("checkStatus sets loading to true during request", async () => {
    112     let resolveFetch!: (v: unknown) => void;
    113     vi.stubGlobal("fetch", vi.fn().mockReturnValue(
    114       new Promise((resolve) => { resolveFetch = resolve; }),
    115     ));
    116     const checkPromise = useAuthStore.getState().checkStatus();
    117     expect(useAuthStore.getState().loading).toBe(true);
    118     resolveFetch({ ok: true, json: async () => ({ configured: false, authenticated: false }) });
    119     await checkPromise;
    120     expect(useAuthStore.getState().loading).toBe(false);
    121   });
    122 
    123   it("checkStatus handles error by setting loading to false", async () => {
    124     vi.stubGlobal("fetch", vi.fn().mockRejectedValue(new Error("network error")));
    125     await useAuthStore.getState().checkStatus();
    126     expect(useAuthStore.getState().loading).toBe(false);
    127     expect(useAuthStore.getState().configured).toBe(false);
    128     expect(useAuthStore.getState().authenticated).toBe(false);
    129   });
    130 
    131   it("checkStatus handles non-Error rejection", async () => {
    132     vi.stubGlobal("fetch", vi.fn().mockRejectedValue("string error"));
    133     await useAuthStore.getState().checkStatus();
    134     expect(useAuthStore.getState().loading).toBe(false);
    135   });
    136 
    137   it("login sends password in body", async () => {
    138     const mockFetch = vi.fn().mockResolvedValue({
    139       ok: true, json: async () => ({}),
    140     });
    141     vi.stubGlobal("fetch", mockFetch);
    142     await useAuthStore.getState().login("my-password");
    143     const callBody = JSON.parse(mockFetch.mock.calls[0][1].body);
    144     expect(callBody.password).toBe("my-password");
    145   });
    146 });