From d55a1ed6486875eeaa9dbaa2ec8357c1f792a278 Mon Sep 17 00:00:00 2001 From: lila Date: Sat, 30 May 2026 03:47:59 +0200 Subject: [PATCH] wip --- .../src/gameSessionStore/GameSessionStore.ts | 2 +- .../api/src/middleware/authMiddleware.test.ts | 103 ++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 apps/api/src/middleware/authMiddleware.test.ts diff --git a/apps/api/src/gameSessionStore/GameSessionStore.ts b/apps/api/src/gameSessionStore/GameSessionStore.ts index 3e6c5d2..271d733 100644 --- a/apps/api/src/gameSessionStore/GameSessionStore.ts +++ b/apps/api/src/gameSessionStore/GameSessionStore.ts @@ -1,6 +1,6 @@ export type GameSessionData = { answers: Map; - userId: string; + userId: string | null; }; export interface GameSessionStore { diff --git a/apps/api/src/middleware/authMiddleware.test.ts b/apps/api/src/middleware/authMiddleware.test.ts new file mode 100644 index 0000000..db79ea2 --- /dev/null +++ b/apps/api/src/middleware/authMiddleware.test.ts @@ -0,0 +1,103 @@ +import express from "express"; +import request from "supertest"; +import { describe, it, expect, vi, beforeEach } from "vitest"; +import type { Session, User } from "better-auth"; + +vi.mock("../lib/auth.js", () => ({ auth: { api: { getSession: vi.fn() } } })); + +vi.mock("better-auth/node", () => ({ + fromNodeHeaders: vi.fn().mockReturnValue({}), +})); + +import { auth } from "../lib/auth.js"; +import { requireAuth, optionalAuth } from "./authMiddleware.js"; + +const mockGetSession = vi.mocked(auth.api.getSession); + +function createOptionalAuthApp() { + const app = express(); + app.use(optionalAuth); + app.get("/test", (req, res) => { + res + .status(200) + .json({ + hasSession: !!req.session, + userId: req.session?.user?.id ?? null, + }); + }); + return app; +} + +describe("optionalAuth", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("allows the request through when no session exists (guest)", async () => { + mockGetSession.mockResolvedValue(null); + + const app = createOptionalAuthApp(); + const res = await request(app).get("/test"); + + expect(res.status).toBe(200); + expect(res.body).toEqual({ hasSession: false, userId: null }); + }); + + it("attaches session to req when user is authenticated", async () => { + mockGetSession.mockResolvedValue({ + session: { id: "session-1" } as Session, + user: { id: "user-1" } as User, + }); + + const app = createOptionalAuthApp(); + const res = await request(app).get("/test"); + + expect(res.status).toBe(200); + expect(res.body).toEqual({ hasSession: true, userId: "user-1" }); + }); + + it("allows the request through even when getSession throws", async () => { + mockGetSession.mockRejectedValue(new Error("auth service down")); + + const app = createOptionalAuthApp(); + const res = await request(app).get("/test"); + + expect(res.status).toBe(200); + expect(res.body).toEqual({ hasSession: false, userId: null }); + }); +}); + +describe("requireAuth", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("returns 401 when no session exists", async () => { + mockGetSession.mockResolvedValue(null); + + const app = express(); + app.use(requireAuth); + app.get("/test", (_req, res) => res.status(200).json({ ok: true })); + + const res = await request(app).get("/test"); + expect(res.status).toBe(401); + expect(res.body).toEqual({ success: false, error: "Unauthorized" }); + }); + + it("allows the request through when session exists", async () => { + mockGetSession.mockResolvedValue({ + session: { id: "session-1" } as Session, + user: { id: "user-1" } as User, + }); + + const app = express(); + app.use(requireAuth); + app.get("/test", (req, res) => { + res.status(200).json({ userId: req.session?.user?.id }); + }); + + const res = await request(app).get("/test"); + expect(res.status).toBe(200); + expect(res.body).toEqual({ userId: "user-1" }); + }); +});