Files
CapaKraken/packages/api/src/__tests__/timeline-allocation-shift-support.test.ts
T

147 lines
4.2 KiB
TypeScript

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<typeof import("@capakraken/application")>();
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,
},
},
});
});
});