import { TRPCError } from "@trpc/server"; import { beforeEach, describe, expect, it, vi } from "vitest"; const { loadAllocationEntryMock, updateAllocationEntryMock, calculateTimelineAllocationDailyCostMock, } = vi.hoisted(() => ({ loadAllocationEntryMock: vi.fn(), updateAllocationEntryMock: vi.fn(), calculateTimelineAllocationDailyCostMock: vi.fn(), })); vi.mock("@capakraken/application", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, loadAllocationEntry: loadAllocationEntryMock, updateAllocationEntry: updateAllocationEntryMock, }; }); vi.mock("../router/timeline-cost-support.js", () => ({ calculateTimelineAllocationDailyCost: calculateTimelineAllocationDailyCostMock, })); import { applyTimelineInlineAllocationUpdate } from "../router/timeline-allocation-inline-support.js"; describe("timeline allocation inline support", () => { beforeEach(() => { vi.clearAllMocks(); }); it("updates staffed allocations with recalculated daily cost and audit changes", async () => { loadAllocationEntryMock.mockResolvedValue({ entry: { id: "allocation_1", hoursPerDay: 4, startDate: new Date("2026-04-01T00:00:00.000Z"), endDate: new Date("2026-04-05T00:00:00.000Z"), metadata: {}, }, resourceId: "resource_1", }); calculateTimelineAllocationDailyCostMock.mockResolvedValue(54000); updateAllocationEntryMock.mockResolvedValue({ allocation: { id: "allocation_1", projectId: "project_1", resourceId: "resource_1", }, }); const tx = { auditLog: { create: vi.fn().mockResolvedValue({}), }, }; const db = { resource: { findUnique: vi.fn().mockResolvedValue({ id: "resource_1", lcrCents: 5000, availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 }, }), }, $transaction: vi.fn(async (callback: (client: typeof tx) => unknown) => callback(tx)), }; const result = await applyTimelineInlineAllocationUpdate({ db: db as never, allocationId: "allocation_1", hoursPerDay: 6, endDate: new Date("2026-04-06T00:00:00.000Z"), includeSaturday: true, role: "Architect", }); expect(result).toEqual({ id: "allocation_1", projectId: "project_1", resourceId: "resource_1", }); expect(calculateTimelineAllocationDailyCostMock).toHaveBeenCalledWith( expect.objectContaining({ db, resourceId: "resource_1", lcrCents: 5000, hoursPerDay: 6, includeSaturday: true, }), ); expect(updateAllocationEntryMock).toHaveBeenCalledWith( tx, { id: "allocation_1", demandRequirementUpdate: { hoursPerDay: 6, startDate: new Date("2026-04-01T00:00:00.000Z"), endDate: new Date("2026-04-06T00:00:00.000Z"), metadata: { includeSaturday: true }, role: "Architect", }, assignmentUpdate: { hoursPerDay: 6, startDate: new Date("2026-04-01T00:00:00.000Z"), endDate: new Date("2026-04-06T00:00:00.000Z"), dailyCostCents: 54000, metadata: { includeSaturday: true }, role: "Architect", }, }, ); expect(tx.auditLog.create).toHaveBeenCalledWith({ data: expect.objectContaining({ entityType: "Allocation", entityId: "allocation_1", action: "UPDATE", }), }); }); it("updates placeholder allocations without recalculating cost", async () => { loadAllocationEntryMock.mockResolvedValue({ entry: { id: "demand_1", hoursPerDay: 4, startDate: new Date("2026-04-01T00:00:00.000Z"), endDate: new Date("2026-04-05T00:00:00.000Z"), metadata: {}, }, resourceId: null, }); updateAllocationEntryMock.mockResolvedValue({ allocation: { id: "demand_1", projectId: "project_1", resourceId: null, }, }); const tx = { auditLog: { create: vi.fn().mockResolvedValue({}), }, }; const db = { resource: { findUnique: vi.fn(), }, $transaction: vi.fn(async (callback: (client: typeof tx) => unknown) => callback(tx)), }; await expect(applyTimelineInlineAllocationUpdate({ db: db as never, allocationId: "demand_1", hoursPerDay: 5, })).resolves.toEqual({ id: "demand_1", projectId: "project_1", resourceId: null, }); expect(db.resource.findUnique).not.toHaveBeenCalled(); expect(calculateTimelineAllocationDailyCostMock).not.toHaveBeenCalled(); }); it("throws when a staffed allocation references a missing resource", async () => { loadAllocationEntryMock.mockResolvedValue({ entry: { id: "allocation_1", hoursPerDay: 4, startDate: new Date("2026-04-01T00:00:00.000Z"), endDate: new Date("2026-04-05T00:00:00.000Z"), metadata: {}, }, resourceId: "resource_1", }); await expect(applyTimelineInlineAllocationUpdate({ db: { resource: { findUnique: vi.fn().mockResolvedValue(null), }, $transaction: vi.fn(), } as never, allocationId: "allocation_1", })).rejects.toThrowError(new TRPCError({ code: "NOT_FOUND", message: "Resource not found", })); }); });