/** * Integration tests for the Terms & Conditions admin CRUD (issue #11). * Covers create/update/toggle/delete + the manage_terms guard, and the * grouping helper used to feed the PO T&C dropdowns. */ 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 { createTerm, updateTerm, toggleTermActive, deleteTerm, } from "@/app/(portal)/admin/terms/actions"; import { getActiveTermsByCategory } from "@/lib/terms-data"; import { makeSession, fd } from "./helpers"; const mockedAuth = vi.mocked(auth); const PREFIX = "INTTEST_TERMS_"; const asManager = () => mockedAuth.mockResolvedValue(makeSession("u-mgr", "MANAGER") as never); afterAll(async () => { await db.termsCondition.deleteMany({ where: { text: { startsWith: PREFIX } } }); }); describe("createTerm", () => { it("persists a clause under its category", async () => { asManager(); const result = await createTerm(fd({ category: "DELIVERY", text: `${PREFIX}Within 2 days` })); expect(result).toEqual({ ok: true }); const t = await db.termsCondition.findFirstOrThrow({ where: { text: `${PREFIX}Within 2 days` } }); expect(t.category).toBe("DELIVERY"); expect(t.isActive).toBe(true); }); it("requires text and a valid category", async () => { asManager(); expect("error" in (await createTerm(fd({ category: "DELIVERY", text: " " })))).toBe(true); expect("error" in (await createTerm(fd({ category: "NOT_A_CATEGORY", text: "x" })))).toBe(true); }); it("refuses callers without manage_terms", async () => { mockedAuth.mockResolvedValue(makeSession("u-tech", "TECHNICAL") as never); expect(await createTerm(fd({ category: "DELIVERY", text: `${PREFIX}nope` }))).toEqual({ error: "Forbidden" }); mockedAuth.mockResolvedValue(makeSession("u-acc", "ACCOUNTS") as never); expect(await createTerm(fd({ category: "DELIVERY", text: `${PREFIX}nope` }))).toEqual({ error: "Forbidden" }); }); }); describe("updateTerm / toggle / delete", () => { it("edits, toggles active, then deletes a clause", async () => { asManager(); await createTerm(fd({ category: "PAYMENT_TERMS", text: `${PREFIX}old wording` })); const t = await db.termsCondition.findFirstOrThrow({ where: { text: `${PREFIX}old wording` } }); expect(await updateTerm(t.id, fd({ category: "INSPECTION", text: `${PREFIX}new wording` }))).toEqual({ ok: true }); const after = await db.termsCondition.findUniqueOrThrow({ where: { id: t.id } }); expect(after.text).toBe(`${PREFIX}new wording`); expect(after.category).toBe("INSPECTION"); expect(await toggleTermActive(t.id)).toEqual({ ok: true }); expect((await db.termsCondition.findUniqueOrThrow({ where: { id: t.id } })).isActive).toBe(false); expect(await deleteTerm(t.id)).toEqual({ ok: true }); expect(await db.termsCondition.findUnique({ where: { id: t.id } })).toBeNull(); }); it("guards update/toggle/delete behind the permission", async () => { mockedAuth.mockResolvedValue(makeSession("u-tech", "TECHNICAL") as never); expect(await updateTerm("x", fd({ category: "DELIVERY", text: "y" }))).toEqual({ error: "Forbidden" }); expect(await toggleTermActive("x")).toEqual({ error: "Forbidden" }); expect(await deleteTerm("x")).toEqual({ error: "Forbidden" }); }); }); describe("getActiveTermsByCategory", () => { it("groups only active clauses by category", async () => { asManager(); await createTerm(fd({ category: "DISPATCH", text: `${PREFIX}active dispatch` })); await createTerm(fd({ category: "DISPATCH", text: `${PREFIX}inactive dispatch` })); const inactive = await db.termsCondition.findFirstOrThrow({ where: { text: `${PREFIX}inactive dispatch` } }); await toggleTermActive(inactive.id); const map = await getActiveTermsByCategory(); expect(map.DISPATCH).toContain(`${PREFIX}active dispatch`); expect(map.DISPATCH).not.toContain(`${PREFIX}inactive dispatch`); }); });