|
|
@@ -0,0 +1,146 @@
|
|
|
+import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
|
+
|
|
|
+// Mock next/headers to provide a simple in-memory cookie store
|
|
|
+vi.mock("next/headers", () => {
|
|
|
+ let store = new Map();
|
|
|
+
|
|
|
+ return {
|
|
|
+ cookies() {
|
|
|
+ return {
|
|
|
+ get(name) {
|
|
|
+ const entry = store.get(name);
|
|
|
+ if (!entry) return undefined;
|
|
|
+ return { name, value: entry.value };
|
|
|
+ },
|
|
|
+ set(name, value, options) {
|
|
|
+ store.set(name, { value, options });
|
|
|
+ },
|
|
|
+ };
|
|
|
+ },
|
|
|
+ __cookieStore: {
|
|
|
+ clear() {
|
|
|
+ store = new Map();
|
|
|
+ },
|
|
|
+ dump() {
|
|
|
+ return store;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ };
|
|
|
+});
|
|
|
+
|
|
|
+// Import after the mock so the module under test uses the mocked cookies()
|
|
|
+import {
|
|
|
+ createSession,
|
|
|
+ getSession,
|
|
|
+ destroySession,
|
|
|
+ SESSION_COOKIE_NAME,
|
|
|
+ SESSION_MAX_AGE_SECONDS,
|
|
|
+} from "./session";
|
|
|
+import { __cookieStore } from "next/headers";
|
|
|
+
|
|
|
+describe("auth session utilities", () => {
|
|
|
+ beforeEach(() => {
|
|
|
+ __cookieStore.clear();
|
|
|
+ process.env.SESSION_SECRET = "test-session-secret";
|
|
|
+ process.env.NODE_ENV = "test";
|
|
|
+ });
|
|
|
+
|
|
|
+ it("creates a session cookie with a signed JWT", async () => {
|
|
|
+ const jwt = await createSession({
|
|
|
+ userId: "user123",
|
|
|
+ role: "branch",
|
|
|
+ branchId: "NL01",
|
|
|
+ });
|
|
|
+
|
|
|
+ expect(typeof jwt).toBe("string");
|
|
|
+ expect(jwt.length).toBeGreaterThan(10);
|
|
|
+
|
|
|
+ const store = __cookieStore.dump();
|
|
|
+ const cookie = store.get(SESSION_COOKIE_NAME);
|
|
|
+
|
|
|
+ expect(cookie).toBeDefined();
|
|
|
+ expect(cookie.value).toBe(jwt);
|
|
|
+
|
|
|
+ expect(cookie.options).toMatchObject({
|
|
|
+ httpOnly: true,
|
|
|
+ secure: false, // NODE_ENV = "test"
|
|
|
+ sameSite: "lax",
|
|
|
+ path: "/",
|
|
|
+ maxAge: SESSION_MAX_AGE_SECONDS,
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it("reads a valid session from cookie", async () => {
|
|
|
+ await createSession({
|
|
|
+ userId: "user456",
|
|
|
+ role: "admin",
|
|
|
+ branchId: null,
|
|
|
+ });
|
|
|
+
|
|
|
+ const session = await getSession();
|
|
|
+
|
|
|
+ expect(session).toEqual({
|
|
|
+ userId: "user456",
|
|
|
+ role: "admin",
|
|
|
+ branchId: null,
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ it("returns null when no session cookie is present", async () => {
|
|
|
+ const session = await getSession();
|
|
|
+ expect(session).toBeNull();
|
|
|
+ });
|
|
|
+
|
|
|
+ it("returns null and clears cookie when token is invalid", async () => {
|
|
|
+ // Manually set an invalid JWT value
|
|
|
+ const store = __cookieStore.dump();
|
|
|
+ store.set(SESSION_COOKIE_NAME, {
|
|
|
+ value: "not-a-valid-jwt",
|
|
|
+ options: {
|
|
|
+ httpOnly: true,
|
|
|
+ secure: false,
|
|
|
+ sameSite: "lax",
|
|
|
+ path: "/",
|
|
|
+ maxAge: SESSION_MAX_AGE_SECONDS,
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ const session = await getSession();
|
|
|
+ expect(session).toBeNull();
|
|
|
+
|
|
|
+ const updatedStore = __cookieStore.dump();
|
|
|
+ const cookie = updatedStore.get(SESSION_COOKIE_NAME);
|
|
|
+
|
|
|
+ expect(cookie).toBeDefined();
|
|
|
+ expect(cookie.value).toBe("");
|
|
|
+ expect(cookie.options.maxAge).toBe(0);
|
|
|
+ });
|
|
|
+
|
|
|
+ it("destroySession clears the session cookie when it exists", async () => {
|
|
|
+ await createSession({
|
|
|
+ userId: "user789",
|
|
|
+ role: "branch",
|
|
|
+ branchId: "NL02",
|
|
|
+ });
|
|
|
+
|
|
|
+ destroySession();
|
|
|
+
|
|
|
+ const store = __cookieStore.dump();
|
|
|
+ const cookie = store.get(SESSION_COOKIE_NAME);
|
|
|
+
|
|
|
+ expect(cookie).toBeDefined();
|
|
|
+ expect(cookie.value).toBe("");
|
|
|
+ expect(cookie.options.maxAge).toBe(0);
|
|
|
+ });
|
|
|
+
|
|
|
+ it("destroySession sets an empty cookie even if none existed before", () => {
|
|
|
+ destroySession();
|
|
|
+
|
|
|
+ const store = __cookieStore.dump();
|
|
|
+ const cookie = store.get(SESSION_COOKIE_NAME);
|
|
|
+
|
|
|
+ expect(cookie).toBeDefined();
|
|
|
+ expect(cookie.value).toBe("");
|
|
|
+ expect(cookie.options.maxAge).toBe(0);
|
|
|
+ });
|
|
|
+});
|