import { afterEach, describe, expect, it } from "vitest"; import { addMockInvoiceComment, areMocksEnabled, getMockApprovalPolicies, getMockApprovals, getMockDepartments, getMockGlAccounts, getMockInvoice, getMockInvoices, getMockPendingApprovals, getMockSessionUser, getMockUsers, getMockVendors, resetMockApprovalPolicies, resetMockApprovals, resetMockDepartments, resetMockGlAccounts, resetMockInvoices, resetMockSession, resetMockUsers, resetMockVendors, setMockSessionUserId, transitionMockInvoiceStatus, updateMockApprovalStatus, updateMockInvoiceHeader, updateMockInvoiceLines, updateMockInvoicePayDate, upsertMockDepartment, upsertMockGlAccount, upsertMockVendor, upsertMockUser, upsertMockApprovalPolicy, } from "@/mocks"; describe("mocks module", () => { afterEach(() => { resetMockInvoices(); resetMockVendors(); resetMockGlAccounts(); resetMockDepartments(); resetMockUsers(); resetMockApprovalPolicies(); resetMockApprovals(); resetMockSession(); }); it("defaults to mocks enabled", () => { expect(areMocksEnabled()).toBe(true); }); it("exposes sample invoices and vendors", () => { expect(getMockVendors().length).toBeGreaterThan(0); expect(getMockInvoices().length).toBeGreaterThanOrEqual(6); }); it("seeds Stampli catalog fields on every invoice (AP-47)", () => { const invoices = getMockInvoices(); expect(invoices.length).toBeGreaterThan(0); for (const invoice of invoices) { expect(typeof invoice.hasBolt).toBe("boolean"); expect(typeof invoice.hasBell).toBe("boolean"); expect(typeof invoice.hasEye).toBe("boolean"); expect(invoice.actionLabel.length).toBeGreaterThan(0); expect(invoice.lastUpdate).toMatch(/^\d{4}-\d{2}-\d{2}$/); expect(invoice.lastUpdateMessage.length).toBeGreaterThan(0); expect(invoice.lastUpdateActorName.length).toBeGreaterThan(0); expect(invoice.lastUpdateAt.length).toBeGreaterThan(0); expect(invoice.invoiceDate).toMatch(/^\d{4}-\d{2}-\d{2}$/); expect(invoice.invoiceDate).not.toBe(invoice.dueDate); expect(invoice.processingBegan).toMatch(/^\d{4}-\d{2}-\d{2}$/); expect(typeof invoice.tagging).toBe("string"); expect(typeof invoice.fs).toBe("string"); expect(typeof invoice.description).toBe("string"); expect(typeof invoice.invoiceType).toBe("string"); expect(typeof invoice.location).toBe("string"); expect(typeof invoice.paymentAccount).toBe("string"); expect(invoice.relatedInvoice === null || typeof invoice.relatedInvoice === "string").toBe( true, ); expect(invoice.rowCount).toBe(invoice.lines.length); } const pending = invoices.filter((invoice) => invoice.status === "pending_approval"); expect(pending.every((invoice) => invoice.actionLabel === "Approve invoice")).toBe(true); const nonPending = invoices.filter((invoice) => invoice.status !== "pending_approval"); expect(nonPending.every((invoice) => invoice.actionLabel !== "Approve invoice")).toBe(true); expect( invoices .filter((invoice) => invoice.status === "approved") .every((invoice) => invoice.actionLabel === "Approved"), ).toBe(true); expect( invoices .filter((invoice) => invoice.status === "paid") .every((invoice) => invoice.actionLabel === "Paid"), ).toBe(true); expect( invoices .filter((invoice) => invoice.status === "scheduled") .every((invoice) => invoice.actionLabel === "Scheduled"), ).toBe(true); expect( invoices .filter((invoice) => invoice.status === "rejected") .every((invoice) => invoice.actionLabel === "Rejected"), ).toBe(true); }); it("seeds at least two invoices per dashboard tab", () => { const invoices = getMockInvoices(); const pending = invoices.filter((invoice) => invoice.status === "pending_approval"); const ready = invoices.filter((invoice) => invoice.status === "approved"); expect(pending.length).toBeGreaterThanOrEqual(2); expect(ready.length).toBeGreaterThanOrEqual(2); expect(invoices.every((invoice) => invoice.invoiceNumbers && invoice.amount >= 0)).toBe(true); }); it("updates pay date in the mutable mock store", () => { const before = getMockInvoices().find((invoice) => invoice.id === "inv-1"); expect(before).toBeDefined(); const updated = updateMockInvoicePayDate("inv-1", "2026-08-18"); expect(updated.payDate).toBe("2026-08-18"); expect(updated.lastUpdateMessage).toBe("Pay date updated"); expect(updated.lastUpdateAt).not.toBe(before?.lastUpdateAt); const after = getMockInvoices().find((invoice) => invoice.id === "inv-1"); expect(after?.payDate).toBe("2026-08-18"); }); it("throws when updating an unknown invoice", () => { expect(() => updateMockInvoicePayDate("missing", "2026-08-18")).toThrow( /Mock invoice not found/, ); }); it("returns invoice detail with lines, comments, and activity", () => { const invoice = getMockInvoice("inv-2"); expect(invoice).toBeDefined(); expect(invoice?.documentUrl).toBe("/fixtures/sample-invoice.pdf"); expect(invoice?.lines.length).toBeGreaterThan(0); expect(invoice?.comments.length).toBeGreaterThan(0); expect(invoice?.activity.length).toBeGreaterThan(0); }); it("updates header and lines coding against mocks", () => { const updated = updateMockInvoiceHeader("inv-2", { memo: "Updated memo", amountDue: 4700 }); expect(updated.memo).toBe("Updated memo"); expect(updated.amountDue).toBe(4700); expect(updated.description).toBe("Updated memo"); const withLines = updateMockInvoiceLines("inv-2", [ { id: "inv-2-line-1", description: "Updated line", amount: 4700, glAccountId: "gl-2", departmentId: "dept-1", }, ]); expect(withLines.lines).toHaveLength(1); expect(withLines.lines[0].description).toBe("Updated line"); expect(withLines.rowCount).toBe(1); expect(getMockInvoice("inv-2")?.lines[0].description).toBe("Updated line"); expect(getMockInvoice("inv-2")?.rowCount).toBe(1); }); it("adds comments and transitions status with activity", () => { const withComment = addMockInvoiceComment("inv-2", { authorUserId: "user-1", authorName: "Alex Rivera", body: "Need another look", createdAt: "2026-08-11T09:00:00.000Z", }); expect(withComment.comments.some((comment) => comment.body === "Need another look")).toBe(true); const approved = transitionMockInvoiceStatus("inv-2", "approved", { actorUserId: "user-2", actorName: "Jordan Lee", message: "Approved", createdAt: "2026-08-12T09:00:00.000Z", }); expect(approved.status).toBe("approved"); expect(approved.activity.some((entry) => entry.message === "Approved")).toBe(true); expect(approved.lastUpdateMessage).toBe("Approved"); expect(approved.lastUpdateActorName).toBe("Jordan Lee"); expect(approved.lastUpdateAt).toBe("2026-08-12T09:00:00.000Z"); expect(approved.lastUpdate).toBe("2026-08-12"); expect(approved.actionLabel).not.toBe("Approve invoice"); expect(getMockPendingApprovals().some((item) => item.invoiceId === "inv-2")).toBe(false); expect(getMockApprovals().find((item) => item.invoiceId === "inv-2")?.status).toBe("approved"); }); it("refreshes last update from a comment and payment account from the method", () => { const commented = addMockInvoiceComment("inv-1", { authorUserId: "user-1", authorName: "Alex Rivera", body: "Checked the total", createdAt: "2026-09-01T15:00:00.000Z", }); expect(commented.lastUpdateAt).toBe("2026-09-01T15:00:00.000Z"); expect(commented.lastUpdate).toBe("2026-09-01"); expect(commented.lastUpdateMessage).toBe("Checked the total"); expect(commented.lastUpdateActorName).toBe("Alex Rivera"); const method = updateMockInvoiceHeader("inv-1", { paymentMethod: "ach" }); expect(method.paymentAccount).toBe("Operating ACH"); expect(getMockInvoice("inv-1")?.paymentAccount).toBe("Operating ACH"); }); it("refreshes description and rowCount when header or lines change", () => { const before = getMockInvoice("inv-1"); expect(before?.rowCount).toBe(2); expect(before?.description).toBe("Office supplies restock"); const header = updateMockInvoiceHeader("inv-1", { memo: "Revised supplies memo" }); expect(header.description).toBe("Revised supplies memo"); const lines = updateMockInvoiceLines("inv-1", [ { id: "inv-1-line-1", description: "Only line", amount: 100, glAccountId: "gl-1", departmentId: "dept-4", }, ]); expect(lines.rowCount).toBe(1); expect(getMockInvoice("inv-1")?.rowCount).toBe(1); }); it("returns undefined for unknown invoice ids", () => { expect(getMockInvoice("missing-invoice")).toBeUndefined(); }); it("switches mock session user", () => { expect(getMockSessionUser().id).toBe("user-1"); const next = setMockSessionUserId("user-2"); expect(next.role).toBe("approver"); expect(getMockSessionUser().id).toBe("user-2"); }); it("includes inv-8 referenced by approvals", () => { expect(getMockInvoice("inv-8")).toBeDefined(); expect(getMockApprovals().some((item) => item.invoiceId === "inv-8")).toBe(true); }); it("seeds settings entities and supports upsert", () => { expect(getMockVendors().length).toBeGreaterThanOrEqual(4); expect(getMockGlAccounts().length).toBeGreaterThanOrEqual(3); expect(getMockDepartments().length).toBeGreaterThanOrEqual(3); expect(getMockUsers().length).toBeGreaterThanOrEqual(3); expect(getMockApprovalPolicies().length).toBeGreaterThanOrEqual(3); const vendor = upsertMockVendor({ id: "vendor-new", name: "New Vendor", email: "new@example.com", defaultPaymentMethod: "ach", }); expect(getMockVendors().some((row) => row.id === vendor.id)).toBe(true); upsertMockGlAccount({ id: "gl-new", code: "9999", name: "Misc" }); expect(getMockGlAccounts().some((row) => row.id === "gl-new")).toBe(true); upsertMockDepartment({ id: "dept-new", code: "NEW", name: "New Dept" }); expect(getMockDepartments().some((row) => row.id === "dept-new")).toBe(true); upsertMockUser({ id: "user-new", name: "New User", email: "new.user@seahaven.example", role: "approver", }); expect(getMockUsers().some((row) => row.id === "user-new")).toBe(true); upsertMockApprovalPolicy({ id: "policy-new", name: "Ad hoc", amountThreshold: 250, approverRole: "admin", }); expect(getMockApprovalPolicies().some((row) => row.id === "policy-new")).toBe(true); }); it("lists pending approvals and updates status", () => { const pending = getMockPendingApprovals(); expect(pending.length).toBeGreaterThanOrEqual(2); expect(getMockApprovals().length).toBeGreaterThanOrEqual(pending.length); const first = pending[0]; const updated = updateMockApprovalStatus(first.id, "approved"); expect(updated.status).toBe("approved"); expect(getMockPendingApprovals().some((row) => row.id === first.id)).toBe(false); }); it("throws when updating an unknown approval", () => { expect(() => updateMockApprovalStatus("missing", "rejected")).toThrow( /Mock approval not found/, ); }); });