/** * Unit tests for GET /api/reports/allocations — A01 role-check enforcement. * * Heavy dependencies (Prisma, PDF renderer, XLSX builder) are mocked so the * test focuses purely on authentication and authorisation behaviour. */ import { describe, expect, it, vi } from "vitest"; // ─── auth mock ──────────────────────────────────────────────────────────────── const authMock = vi.hoisted(() => vi.fn()); vi.mock("~/server/auth.js", () => ({ auth: authMock })); // ─── heavy dep stubs ───────────────────────────────────────────────────────── vi.mock("@capakraken/db", () => ({ prisma: { demandRequirement: { findMany: vi.fn().mockResolvedValue([]) }, assignment: { findMany: vi.fn().mockResolvedValue([]) }, user: { findMany: vi.fn().mockResolvedValue([]) }, }, })); vi.mock("@capakraken/application", () => ({ buildSplitAllocationReadModel: vi.fn().mockReturnValue({ assignments: [] }), })); vi.mock("@capakraken/api", () => ({ anonymizeResource: vi.fn((r: unknown) => r), getAnonymizationDirectory: vi.fn().mockResolvedValue({}), })); vi.mock("@react-pdf/renderer", () => ({ renderToBuffer: vi.fn().mockResolvedValue(Buffer.from("PDF")), })); vi.mock("react", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, createElement: vi.fn().mockReturnValue(null) }; }); vi.mock("~/lib/workbook-export.js", () => ({ createWorkbookArrayBuffer: vi.fn().mockResolvedValue(new ArrayBuffer(8)), })); vi.mock("~/components/reports/AllocationReport.js", () => ({ AllocationReport: () => null, })); // ─── import route after mocks ───────────────────────────────────────────────── import { GET } from "./route.js"; function makeRequest(url = "http://localhost/api/reports/allocations") { return new Request(url); } // ─── tests ─────────────────────────────────────────────────────────────────── describe("GET /api/reports/allocations — authentication", () => { it("returns 401 when no session exists", async () => { authMock.mockResolvedValue(null); const res = await GET(makeRequest()); expect(res.status).toBe(401); }); it("returns 401 when session has no user", async () => { authMock.mockResolvedValue({ user: null }); const res = await GET(makeRequest()); expect(res.status).toBe(401); }); }); describe("GET /api/reports/allocations — authorisation (A01)", () => { it("returns 403 for USER role", async () => { authMock.mockResolvedValue({ user: { email: "user@example.com", role: "USER" } }); const res = await GET(makeRequest()); expect(res.status).toBe(403); }); it("returns 403 for VIEWER role", async () => { authMock.mockResolvedValue({ user: { email: "viewer@example.com", role: "VIEWER" } }); const res = await GET(makeRequest()); expect(res.status).toBe(403); }); it("returns 403 when role is absent from session", async () => { authMock.mockResolvedValue({ user: { email: "noRole@example.com" } }); const res = await GET(makeRequest()); expect(res.status).toBe(403); }); it("returns 200 for ADMIN role (PDF)", async () => { authMock.mockResolvedValue({ user: { email: "admin@example.com", role: "ADMIN" } }); const res = await GET(makeRequest()); expect(res.status).toBe(200); expect(res.headers.get("Content-Type")).toContain("pdf"); }); it("returns 200 for MANAGER role (PDF)", async () => { authMock.mockResolvedValue({ user: { email: "mgr@example.com", role: "MANAGER" } }); const res = await GET(makeRequest()); expect(res.status).toBe(200); }); it("returns 200 for CONTROLLER role (PDF)", async () => { authMock.mockResolvedValue({ user: { email: "ctrl@example.com", role: "CONTROLLER" } }); const res = await GET(makeRequest()); expect(res.status).toBe(200); }); it("returns 200 for ADMIN role with xlsx format", async () => { authMock.mockResolvedValue({ user: { email: "admin@example.com", role: "ADMIN" } }); const res = await GET(makeRequest("http://localhost/api/reports/allocations?format=xlsx")); expect(res.status).toBe(200); expect(res.headers.get("Content-Type")).toContain("spreadsheetml"); }); });