/** * Integration tests for MANAGER role creating and submitting POs. * Verifies the new create_po / submit_po permissions granted to MANAGER. */ import { vi, describe, it, expect, beforeAll, afterEach } from "vitest"; vi.mock("@/auth", () => ({ auth: vi.fn() })); vi.mock("next/cache", () => ({ revalidatePath: vi.fn() })); vi.mock("@/lib/notifier", () => ({ notify: vi.fn() })); import { auth } from "@/auth"; import { db } from "@/lib/db"; import { createPo } from "@/app/(portal)/po/new/actions"; import { approvepo } from "@/app/(portal)/approvals/[id]/actions"; import { discardDraftPo } from "@/app/(portal)/po/[id]/actions"; import { makeSession, getSeedUser, getSeedVessel, getSeedAccount, getSeedVendor, makePoForm, deletePosByTitle, } from "./helpers"; const PREFIX = "INTTEST_MGR_CREATE_"; let managerId: string; let accountsId: string; let vesselId: string; let accountId: string; let vendorId: string; beforeAll(async () => { const [mgr, acct, vessel, account, vendor] = await Promise.all([ getSeedUser("manager@pelagia.local"), getSeedUser("accounts@pelagia.local"), getSeedVessel("MV Pelagia Star"), getSeedAccount("TECH-OPS"), getSeedVendor("Apar Industries Ltd"), ]); managerId = mgr.id; accountsId = acct.id; vesselId = vessel.id; accountId = account.id; vendorId = vendor.id; }); afterEach(async () => { await deletePosByTitle(PREFIX); }); // ── MANAGER can create POs ──────────────────────────────────────────────────── describe("MANAGER — create PO", () => { it("MANAGER can save a PO as DRAFT", async () => { vi.mocked(auth).mockResolvedValue(makeSession(managerId, "MANAGER")); const form = makePoForm({ title: `${PREFIX}Draft`, vesselId, accountId, intent: "draft" }); const result = await createPo(form); expect(result).not.toHaveProperty("error"); const po = await db.purchaseOrder.findUnique({ where: { id: (result as { id: string }).id } }); expect(po?.status).toBe("DRAFT"); expect(po?.submitterId).toBe(managerId); }); it("MANAGER can submit a PO directly", async () => { vi.mocked(auth).mockResolvedValue(makeSession(managerId, "MANAGER")); const form = makePoForm({ title: `${PREFIX}Submit`, vesselId, accountId, intent: "submit" }); const result = await createPo(form); expect(result).not.toHaveProperty("error"); const po = await db.purchaseOrder.findUnique({ where: { id: (result as { id: string }).id } }); expect(po?.status).toBe("MGR_REVIEW"); expect(po?.submittedAt).not.toBeNull(); }); it("MANAGER can discard their own DRAFT", async () => { vi.mocked(auth).mockResolvedValue(makeSession(managerId, "MANAGER")); const form = makePoForm({ title: `${PREFIX}Discard`, vesselId, accountId, intent: "draft" }); const { id: poId } = (await createPo(form)) as { id: string }; const result = await discardDraftPo(poId); expect(result).toEqual({ ok: true }); expect(await db.purchaseOrder.findUnique({ where: { id: poId } })).toBeNull(); }); it("stores correct submitterId on MANAGER-created PO", async () => { vi.mocked(auth).mockResolvedValue(makeSession(managerId, "MANAGER")); const form = makePoForm({ title: `${PREFIX}SubmitterId`, vesselId, accountId }); const { id: poId } = (await createPo(form)) as { id: string }; const po = await db.purchaseOrder.findUnique({ where: { id: poId } }); expect(po?.submitterId).toBe(managerId); }); }); // ── Negative permission tests ───────────────────────────────────────────────── describe("role — negative permission tests for PO creation", () => { it("ACCOUNTS cannot create a PO", async () => { vi.mocked(auth).mockResolvedValue(makeSession(accountsId, "ACCOUNTS")); const form = makePoForm({ title: `${PREFIX}AcctsForbidden`, vesselId, accountId }); const result = await createPo(form); expect(result).toHaveProperty("error"); }); it("unauthenticated request returns Unauthorized", async () => { vi.mocked(auth).mockResolvedValue(null); const form = makePoForm({ title: `${PREFIX}Unauth`, vesselId, accountId }); const result = await createPo(form); expect(result).toEqual({ error: "Unauthorized" }); }); it("MANAGER cannot approve their own submitted PO (same user)", async () => { // Manager creates and submits vi.mocked(auth).mockResolvedValue(makeSession(managerId, "MANAGER")); const form = makePoForm({ title: `${PREFIX}SelfApprove`, vesselId, accountId, vendorId, intent: "submit", }); const { id: poId } = (await createPo(form)) as { id: string }; // Approving as the same manager — the action itself doesn't block same-user approval // because approval authority is role-based, not submitter-based. // This test documents the current behaviour. const result = await approvepo({ poId }); // Should succeed because MANAGER has approve_po permission and the PO has a vendor expect(result).toEqual({ ok: true }); }); });