Files
kandev/apps/web/lib/state/slices/auth/auth-slice.test.ts
T
Clément Fiere 15c0c35df6 feat(auth): resolve session hostnames (#2741)
Co-authored-by: Carlos Florencio <carlosmflorencio@gmail.com>
2026-09-04 13:06:33 +00:00

95 lines
3.2 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { create } from "zustand";
import { immer } from "zustand/middleware/immer";
import { createAuthSlice, defaultAuthState } from "./auth-slice";
import type { AuthSlice } from "./types";
function makeStore() {
return create<AuthSlice>()(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
immer((...a) => ({ ...(createAuthSlice as any)(...a) })),
);
}
describe("auth slice", () => {
// Production-safety invariant: a boot payload / test harness with no auth
// block at all must not gate the app shell. See auth-slice.ts for why.
it("defaults to disabled mode with authenticated true and no user", () => {
const store = makeStore();
expect(defaultAuthState.auth.mode).toBe("disabled");
expect(defaultAuthState.auth.authenticated).toBe(true);
expect(store.getState().auth.mode).toBe("disabled");
expect(store.getState().auth.authenticated).toBe(true);
expect(store.getState().auth.user).toBeNull();
});
it("setAuthState replaces the whole snapshot", () => {
const store = makeStore();
store.getState().setAuthState({
mode: "enabled",
authenticated: true,
user: {
id: "user-1",
email: "a@b.com",
display_name: "A B",
role: "admin",
status: "active",
},
});
expect(store.getState().auth.mode).toBe("enabled");
expect(store.getState().auth.authenticated).toBe(true);
expect(store.getState().auth.user?.email).toBe("a@b.com");
});
it("clearAuthenticated marks the session unauthenticated and drops the user", () => {
const store = makeStore();
store.getState().setAuthState({
mode: "enabled",
authenticated: true,
user: {
id: "user-1",
email: "a@b.com",
display_name: "A B",
role: "member",
status: "active",
},
});
store.getState().clearAuthenticated();
expect(store.getState().auth.authenticated).toBe(false);
expect(store.getState().auth.user).toBeNull();
// mode is left untouched — clearAuthenticated only reflects session expiry.
expect(store.getState().auth.mode).toBe("enabled");
});
it("keeps newer hostname entries and clears them across identities", () => {
const store = makeStore();
store
.getState()
.setSessionHostname("192.0.2.10", "old.example.test", "2026-08-16T12:00:00.000000001Z");
store
.getState()
.setSessionHostname("192.0.2.10", "new.example.test", "2026-08-16T12:00:00.000000002Z");
store
.getState()
.setSessionHostname("192.0.2.10", "late.example.test", "2026-08-16T12:00:00.000000001Z");
expect(store.getState().sessionHostnames["192.0.2.10"]).toEqual({
hostname: "new.example.test",
resolvedAt: "2026-08-16T12:00:00.000000002Z",
});
const epoch = store.getState().sessionHostnamesEpoch;
store.getState().setAuthState({
mode: "enabled",
authenticated: true,
user: {
id: "user-2",
email: "user@example.test",
display_name: "User",
role: "member",
status: "active",
},
});
expect(store.getState().sessionHostnames).toEqual({});
expect(store.getState().sessionHostnamesEpoch).toBe(epoch + 1);
});
});