import { TRPCError } from "@trpc/server"; import { describe, expect, it, vi } from "vitest"; const { findAllocationEntryMock, updateAllocationEntryMock, } = vi.hoisted(() => ({ findAllocationEntryMock: vi.fn(), updateAllocationEntryMock: vi.fn(), })); vi.mock("@capakraken/application", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, findAllocationEntry: findAllocationEntryMock, updateAllocationEntry: updateAllocationEntryMock, }; }); import { applyTimelineBatchAllocationShift } from "../router/timeline-allocation-shift-support.js"; describe("timeline allocation shift support", () => { it("returns an empty result when the shift delta is zero", async () => { const db = { $transaction: vi.fn(), }; await expect(applyTimelineBatchAllocationShift({ db: db as never, allocationIds: ["allocation_1"], daysDelta: 0, mode: "move", })).resolves.toEqual([]); expect(findAllocationEntryMock).not.toHaveBeenCalled(); expect(db.$transaction).not.toHaveBeenCalled(); }); it("throws when no allocations can be resolved", async () => { findAllocationEntryMock.mockResolvedValue(null); await expect(applyTimelineBatchAllocationShift({ db: { $transaction: vi.fn(), } as never, allocationIds: ["missing_1", "missing_2"], daysDelta: 2, mode: "move", })).rejects.toThrowError(new TRPCError({ code: "NOT_FOUND", message: "No allocations found", })); }); it("updates resolved allocations and writes a batch audit record", async () => { findAllocationEntryMock .mockResolvedValueOnce({ entry: { id: "allocation_1", startDate: new Date("2026-04-01T00:00:00.000Z"), endDate: new Date("2026-04-05T00:00:00.000Z"), }, }) .mockResolvedValueOnce(null) .mockResolvedValueOnce({ entry: { id: "allocation_3", startDate: new Date("2026-04-10T00:00:00.000Z"), endDate: new Date("2026-04-12T00:00:00.000Z"), }, }); updateAllocationEntryMock .mockResolvedValueOnce({ allocation: { id: "allocation_1", projectId: "project_1", resourceId: "resource_1" }, }) .mockResolvedValueOnce({ allocation: { id: "allocation_3", projectId: "project_2", resourceId: "resource_2" }, }); const tx = { auditLog: { create: vi.fn().mockResolvedValue({}), }, }; const db = { $transaction: vi.fn(async (callback: (client: typeof tx) => unknown) => callback(tx)), }; const result = await applyTimelineBatchAllocationShift({ db: db as never, allocationIds: ["allocation_1", "missing_2", "allocation_3"], daysDelta: 2, mode: "move", }); expect(result).toEqual([ { id: "allocation_1", projectId: "project_1", resourceId: "resource_1" }, { id: "allocation_3", projectId: "project_2", resourceId: "resource_2" }, ]); expect(updateAllocationEntryMock).toHaveBeenNthCalledWith( 1, tx, { id: "allocation_1", demandRequirementUpdate: { startDate: new Date("2026-04-03T00:00:00.000Z"), endDate: new Date("2026-04-07T00:00:00.000Z"), }, assignmentUpdate: { startDate: new Date("2026-04-03T00:00:00.000Z"), endDate: new Date("2026-04-07T00:00:00.000Z"), }, }, ); expect(updateAllocationEntryMock).toHaveBeenNthCalledWith( 2, tx, { id: "allocation_3", demandRequirementUpdate: { startDate: new Date("2026-04-12T00:00:00.000Z"), endDate: new Date("2026-04-14T00:00:00.000Z"), }, assignmentUpdate: { startDate: new Date("2026-04-12T00:00:00.000Z"), endDate: new Date("2026-04-14T00:00:00.000Z"), }, }, ); expect(tx.auditLog.create).toHaveBeenCalledWith({ data: { entityType: "Allocation", entityId: "allocation_1,missing_2,allocation_3", action: "UPDATE", changes: { operation: "batchShift", mode: "move", daysDelta: 2, count: 2, }, }, }); }); });