test(integration): green the last 3 behavioural-drift tests (108/108)

- resubmit: updatePo distinguishes intent "resubmit" (from EDITS_REQUESTED)
  from "submit" (from DRAFT); test now sends "resubmit" (makePoForm widened).
- payment: MANAGER now holds process_payment, so the "wrong permission"
  negative test uses TECHNICAL (which lacks it).
- vendor: provideVendorId rejects on a missing vendorId *code*; seeded
  unverified vendors carry codes, so create a genuinely code-less vendor.

Full integration suite: 108/108 passing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hardik 2026-06-21 02:39:41 +05:30
parent b70eec261b
commit 4c53aeecb0
4 changed files with 20 additions and 12 deletions

View file

@ -344,7 +344,7 @@ describe("S-07 — edit and resubmit after edits requested", () => {
await requestEdits({ poId, note: "Update line items" });
vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(techId, "TECHNICAL"));
const form = makePoForm({ title: `${PREFIX}Resubmit`, vesselId, accountId, intent: "submit" });
const form = makePoForm({ title: `${PREFIX}Resubmit`, vesselId, accountId, intent: "resubmit" });
const result = await updatePo(poId, form);
expect(result).toEqual({ id: poId });

View file

@ -58,7 +58,7 @@ export function makePoForm(overrides: {
vesselId: string;
accountId: string;
vendorId?: string;
intent?: "draft" | "submit";
intent?: "draft" | "submit" | "resubmit";
lineItems?: Array<{ description: string; quantity: number; unit: string; unitPrice: number; gstRate?: number }>;
}): FormData {
const form = new FormData();
@ -76,11 +76,12 @@ export function makePoForm(overrides: {
// ── Cleanup helpers ──────────────────────────────────────────────────────────
// POAction has no onDelete: Cascade, so its rows must be removed before the PO.
// (POLineItem / PODocument / Receipt cascade automatically.)
// POAction and Receipt have no onDelete: Cascade, so their rows must be removed
// before the PO. (POLineItem / PODocument cascade automatically.)
async function deletePosByIds(ids: string[]) {
if (ids.length === 0) return;
await db.pOAction.deleteMany({ where: { poId: { in: ids } } });
await db.receipt.deleteMany({ where: { poId: { in: ids } } });
await db.purchaseOrder.deleteMany({ where: { id: { in: ids } } });
}

View file

@ -151,14 +151,14 @@ describe("A-02 — mark PO as paid with reference number", () => {
expect(calls).toContain("PAYMENT_SENT");
});
it("MANAGER role cannot mark as paid (wrong permission)", async () => {
const poId = await createApprovedPo(`${PREFIX}PaidMgrForbidden`);
it("TECHNICAL role cannot mark as paid (no process_payment permission)", async () => {
const poId = await createApprovedPo(`${PREFIX}PaidTechForbidden`);
vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(accountsId, "ACCOUNTS"));
await processPayment({ poId });
vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(managerId, "MANAGER"));
const result = await markPaid({ poId, paymentRef: "MGR-REF", paymentDate: TODAY });
vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(techId, "TECHNICAL"));
const result = await markPaid({ poId, paymentRef: "TECH-REF", paymentDate: TODAY });
expect(result).toHaveProperty("error");
});
});

View file

@ -7,7 +7,7 @@
* - Unverified vendor rejected by provideVendorId
* - AUDITOR cannot provide vendor ID
*/
import { vi, describe, it, expect, beforeAll, afterEach } from "vitest";
import { vi, describe, it, expect, beforeAll, afterAll, afterEach } from "vitest";
vi.mock("@/auth", () => ({ auth: vi.fn() }));
vi.mock("next/cache", () => ({ revalidatePath: vi.fn() }));
@ -66,15 +66,22 @@ beforeAll(async () => {
auditorId = created.id;
}
// Grab an unverified vendor
const unverified = await db.vendor.findFirst({ where: { isVerified: false } });
unverifiedVendorDbId = unverified!.id;
// A vendor with no formal vendorId code — provideVendorId must reject it.
// (Seeded "unverified" vendors can still carry a code, so create a code-less one.)
const noCode = await db.vendor.create({
data: { name: `${PREFIX}NoCodeVendor`, isVerified: false, vendorId: null },
});
unverifiedVendorDbId = noCode.id;
});
afterEach(async () => {
await deletePosByTitle(PREFIX);
});
afterAll(async () => {
await db.vendor.deleteMany({ where: { name: { startsWith: PREFIX } } });
});
async function makeReviewPo(title: string, withVendor = false) {
vi.mocked(auth as unknown as () => Promise<unknown>).mockResolvedValue(makeSession(techId, "TECHNICAL"));
const form = makePoForm({