refactor(api): extract timeline project detail artifact loader
This commit is contained in:
@@ -1,5 +1,18 @@
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("../router/timeline-holiday-read.js", async () => {
|
||||
const actual = await vi.importActual<typeof import("../router/timeline-holiday-read.js")>(
|
||||
"../router/timeline-holiday-read.js",
|
||||
);
|
||||
|
||||
return {
|
||||
...actual,
|
||||
loadTimelineHolidayOverlays: vi.fn(),
|
||||
formatHolidayOverlays: vi.fn(actual.formatHolidayOverlays),
|
||||
};
|
||||
});
|
||||
|
||||
import {
|
||||
buildTimelineBudgetStatusAllocations,
|
||||
buildTimelineBudgetStatusResponse,
|
||||
@@ -9,10 +22,22 @@ import {
|
||||
buildTimelineProjectContextSummary,
|
||||
buildTimelineShiftValidationBookings,
|
||||
buildTimelineShiftPreviewDetailResponse,
|
||||
loadTimelineProjectContextDetailArtifacts,
|
||||
resolveTimelineProjectContextPeriod,
|
||||
} from "../router/timeline-project-context-support.js";
|
||||
import {
|
||||
formatHolidayOverlays,
|
||||
loadTimelineHolidayOverlays,
|
||||
} from "../router/timeline-holiday-read.js";
|
||||
|
||||
const loadTimelineHolidayOverlaysMock = vi.mocked(loadTimelineHolidayOverlays);
|
||||
const formatHolidayOverlaysMock = vi.mocked(formatHolidayOverlays);
|
||||
|
||||
describe("timeline project context support", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("derives the detail period from explicit input and validates order", () => {
|
||||
expect(resolveTimelineProjectContextPeriod({
|
||||
requestedStartDate: "2026-04-03",
|
||||
@@ -451,4 +476,127 @@ describe("timeline project context support", () => {
|
||||
budgetCents: 120000,
|
||||
});
|
||||
});
|
||||
|
||||
it("loads detail artifacts with formatted holiday overlays only when resources exist", async () => {
|
||||
loadTimelineHolidayOverlaysMock.mockResolvedValueOnce([
|
||||
{
|
||||
id: "overlay_1",
|
||||
resourceId: "resource_1",
|
||||
startDate: new Date("2026-04-03T00:00:00.000Z"),
|
||||
endDate: new Date("2026-04-03T00:00:00.000Z"),
|
||||
note: "Holiday",
|
||||
scope: "COUNTRY",
|
||||
calendarName: "DE",
|
||||
sourceType: "CALENDAR",
|
||||
countryCode: "DE",
|
||||
countryName: "Germany",
|
||||
federalState: null,
|
||||
metroCityName: null,
|
||||
},
|
||||
] as never);
|
||||
|
||||
const artifacts = await loadTimelineProjectContextDetailArtifacts({} as never, {
|
||||
projectId: "project_1",
|
||||
requestedStartDate: "2026-04-01",
|
||||
durationDays: 5,
|
||||
projectStartDate: new Date("2026-04-01T00:00:00.000Z"),
|
||||
projectEndDate: new Date("2026-04-10T00:00:00.000Z"),
|
||||
firstAssignmentStartDate: new Date("2026-04-02T00:00:00.000Z"),
|
||||
firstDemandStartDate: new Date("2026-04-01T00:00:00.000Z"),
|
||||
assignments: [
|
||||
{
|
||||
id: "assignment_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { displayName: "Alice" },
|
||||
startDate: new Date("2026-04-01T00:00:00.000Z"),
|
||||
endDate: new Date("2026-04-05T00:00:00.000Z"),
|
||||
hoursPerDay: 8,
|
||||
},
|
||||
],
|
||||
allResourceAllocations: [
|
||||
{
|
||||
id: "booking_1",
|
||||
resourceId: "resource_1",
|
||||
projectId: "project_2",
|
||||
project: { name: "Other", shortCode: "OTH" },
|
||||
startDate: new Date("2026-04-04T00:00:00.000Z"),
|
||||
endDate: new Date("2026-04-07T00:00:00.000Z"),
|
||||
hoursPerDay: 6,
|
||||
status: "ACTIVE",
|
||||
},
|
||||
],
|
||||
resourceIds: ["resource_1"],
|
||||
});
|
||||
|
||||
expect(loadTimelineHolidayOverlaysMock).toHaveBeenCalledWith(
|
||||
{},
|
||||
{
|
||||
startDate: new Date("2026-04-01T00:00:00.000Z"),
|
||||
endDate: new Date("2026-04-10T00:00:00.000Z"),
|
||||
resourceIds: ["resource_1"],
|
||||
projectIds: ["project_1"],
|
||||
},
|
||||
);
|
||||
expect(formatHolidayOverlaysMock).toHaveBeenCalledTimes(1);
|
||||
expect(artifacts).toEqual({
|
||||
period: {
|
||||
startDate: new Date("2026-04-01T00:00:00.000Z"),
|
||||
endDate: new Date("2026-04-10T00:00:00.000Z"),
|
||||
},
|
||||
holidayOverlays: [
|
||||
{
|
||||
id: "overlay_1",
|
||||
resourceId: "resource_1",
|
||||
startDate: "2026-04-03",
|
||||
endDate: "2026-04-03",
|
||||
note: "Holiday",
|
||||
scope: "COUNTRY",
|
||||
calendarName: "DE",
|
||||
sourceType: "CALENDAR",
|
||||
countryCode: "DE",
|
||||
countryName: "Germany",
|
||||
federalState: null,
|
||||
metroCityName: null,
|
||||
},
|
||||
],
|
||||
assignmentConflicts: [
|
||||
{
|
||||
assignmentId: "assignment_1",
|
||||
resourceId: "resource_1",
|
||||
resourceName: "Alice",
|
||||
startDate: "2026-04-01",
|
||||
endDate: "2026-04-05",
|
||||
hoursPerDay: 8,
|
||||
overlapCount: 1,
|
||||
crossProjectOverlapCount: 1,
|
||||
overlaps: [
|
||||
{
|
||||
id: "booking_1",
|
||||
projectId: "project_2",
|
||||
projectName: "Other",
|
||||
projectShortCode: "OTH",
|
||||
startDate: "2026-04-04",
|
||||
endDate: "2026-04-07",
|
||||
hoursPerDay: 6,
|
||||
status: "ACTIVE",
|
||||
sameProject: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const emptyArtifacts = await loadTimelineProjectContextDetailArtifacts({} as never, {
|
||||
projectId: "project_1",
|
||||
requestedStartDate: "2026-04-01",
|
||||
durationDays: 5,
|
||||
assignments: [],
|
||||
allResourceAllocations: [],
|
||||
resourceIds: [],
|
||||
});
|
||||
|
||||
expect(loadTimelineHolidayOverlaysMock).toHaveBeenCalledTimes(1);
|
||||
expect(emptyArtifacts.holidayOverlays).toEqual([]);
|
||||
expect(emptyArtifacts.assignmentConflicts).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user