/** * Integration tests for the Project Codes admin CRUD (issue #124). * Covers create/update/toggle/delete + the manage_project_codes guard. */ import { vi, describe, it, expect, afterAll } from "vitest"; vi.mock("@/auth", () => ({ auth: vi.fn() })); vi.mock("next/cache", () => ({ revalidatePath: vi.fn() })); import { auth } from "@/auth"; import { db } from "@/lib/db"; import { createProjectCode, updateProjectCode, toggleProjectCodeActive, deleteProjectCode, } from "@/app/(portal)/admin/project-codes/actions"; import { makeSession, fd } from "./helpers"; const mockedAuth = vi.mocked(auth); const PREFIX = "INTTEST_PROJCODE_"; const asManager = () => mockedAuth.mockResolvedValue(makeSession("u-mgr", "MANAGER") as never); afterAll(async () => { await db.projectCode.deleteMany({ where: { code: { startsWith: PREFIX } } }); }); describe("createProjectCode", () => { it("persists an active project code", async () => { asManager(); const result = await createProjectCode(fd({ code: `${PREFIX}Alpha` })); expect(result).toEqual({ ok: true }); const code = await db.projectCode.findFirstOrThrow({ where: { code: `${PREFIX}Alpha` } }); expect(code.isActive).toBe(true); }); it("requires a non-empty code", async () => { asManager(); expect("error" in (await createProjectCode(fd({ code: " " })))).toBe(true); }); it("rejects a duplicate code", async () => { asManager(); await createProjectCode(fd({ code: `${PREFIX}Dup` })); const result = await createProjectCode(fd({ code: `${PREFIX}Dup` })); expect("error" in result).toBe(true); }); it("refuses callers without manage_project_codes", async () => { mockedAuth.mockResolvedValue(makeSession("u-tech", "TECHNICAL") as never); expect(await createProjectCode(fd({ code: `${PREFIX}X` }))).toEqual({ error: "Forbidden" }); mockedAuth.mockResolvedValue(makeSession("u-acc", "ACCOUNTS") as never); expect(await createProjectCode(fd({ code: `${PREFIX}X` }))).toEqual({ error: "Forbidden" }); }); }); describe("updateProjectCode / toggle / delete", () => { it("edits, toggles active, then deletes a project code", async () => { asManager(); await createProjectCode(fd({ code: `${PREFIX}Old` })); const code = await db.projectCode.findFirstOrThrow({ where: { code: `${PREFIX}Old` } }); expect(await updateProjectCode(code.id, fd({ code: `${PREFIX}New` }))).toEqual({ ok: true }); expect((await db.projectCode.findUniqueOrThrow({ where: { id: code.id } })).code).toBe(`${PREFIX}New`); expect(await toggleProjectCodeActive(code.id)).toEqual({ ok: true }); expect((await db.projectCode.findUniqueOrThrow({ where: { id: code.id } })).isActive).toBe(false); expect(await deleteProjectCode(code.id)).toEqual({ ok: true }); expect(await db.projectCode.findUnique({ where: { id: code.id } })).toBeNull(); }); it("guards update/toggle/delete behind the permission", async () => { mockedAuth.mockResolvedValue(makeSession("u-tech", "TECHNICAL") as never); expect(await updateProjectCode("x", fd({ code: "y" }))).toEqual({ error: "Forbidden" }); expect(await toggleProjectCodeActive("x")).toEqual({ error: "Forbidden" }); expect(await deleteProjectCode("x")).toEqual({ error: "Forbidden" }); }); });