import { SystemRole } from "@capakraken/shared"; import { beforeEach, describe, expect, it, vi } from "vitest"; const { createAuditEntry } = vi.hoisted(() => ({ createAuditEntry: vi.fn(), })); vi.mock("../lib/audit.js", () => ({ createAuditEntry, })); import { addRateCardLine, createRateCard, replaceRateCardLines, } from "../router/rate-card-procedure-support.js"; function createManagerContext(db: Record) { return { db: db as never, dbUser: { id: "manager_1", systemRole: SystemRole.MANAGER, permissionOverrides: null, }, }; } describe("rate-card procedure support", () => { beforeEach(() => { vi.clearAllMocks(); }); it("creates a rate card and writes a summarized audit entry", async () => { const create = vi.fn().mockResolvedValue({ id: "rc_1", name: "Standard 2026", lines: [{ id: "line_1" }], }); const result = await createRateCard(createManagerContext({ rateCard: { create }, }), { name: "Standard 2026", currency: "EUR", lines: [{ costRateCents: 9_500, billRateCents: 14_000, chapter: "Delivery", attributes: {}, }], }); expect(result).toEqual({ id: "rc_1", name: "Standard 2026", lines: [{ id: "line_1" }], }); expect(create).toHaveBeenCalledWith(expect.objectContaining({ data: expect.objectContaining({ name: "Standard 2026", currency: "EUR", }), })); expect(createAuditEntry).toHaveBeenCalledWith(expect.objectContaining({ entityType: "RateCard", entityId: "rc_1", action: "CREATE", after: { name: "Standard 2026", currency: "EUR", lineCount: 1, }, })); }); it("adds a line and uses the rate card name in audit metadata", async () => { const lineCreate = vi.fn().mockResolvedValue({ id: "line_1", rateCardId: "rc_1", chapter: "Delivery", costRateCents: 9_500, billRateCents: 14_000, }); const result = await addRateCardLine(createManagerContext({ rateCard: { findUnique: vi.fn().mockResolvedValue({ id: "rc_1", name: "Standard 2026" }), }, rateCardLine: { create: lineCreate, }, }), { rateCardId: "rc_1", line: { costRateCents: 9_500, billRateCents: 14_000, chapter: "Delivery", attributes: {}, }, }); expect(result.id).toBe("line_1"); expect(lineCreate).toHaveBeenCalledWith(expect.objectContaining({ data: expect.objectContaining({ rateCardId: "rc_1", chapter: "Delivery", }), })); expect(createAuditEntry).toHaveBeenCalledWith(expect.objectContaining({ entityType: "RateCardLine", entityId: "line_1", entityName: "Standard 2026 - Delivery", })); }); it("replaces lines inside a transaction and audits the replacement count", async () => { const deleteMany = vi.fn().mockResolvedValue({ count: 2 }); const create = vi.fn() .mockResolvedValueOnce({ id: "line_1", rateCardId: "rc_1", costRateCents: 8_000 }) .mockResolvedValueOnce({ id: "line_2", rateCardId: "rc_1", costRateCents: 9_500 }); const db = { rateCard: { findUnique: vi.fn().mockResolvedValue({ id: "rc_1", name: "Standard 2026" }), }, rateCardLine: {}, $transaction: vi.fn(async (callback: (tx: { rateCardLine: { deleteMany: typeof deleteMany; create: typeof create } }) => unknown) => callback({ rateCardLine: { deleteMany, create }, })), }; const result = await replaceRateCardLines(createManagerContext(db), { rateCardId: "rc_1", lines: [ { costRateCents: 8_000, chapter: "Delivery", attributes: {} }, { costRateCents: 9_500, chapter: "Pipeline", attributes: {} }, ], }); expect(deleteMany).toHaveBeenCalledWith({ where: { rateCardId: "rc_1" } }); expect(create).toHaveBeenCalledTimes(2); expect(result).toHaveLength(2); expect(createAuditEntry).toHaveBeenCalledWith(expect.objectContaining({ entityType: "RateCard", entityId: "rc_1", summary: "Replaced all lines with 2 new lines", after: { replacedLineCount: 2 }, })); }); });