refactor(api): extract timeline project context response support
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
buildTimelineProjectContextDetailResponse,
|
||||
buildTimelineProjectContextResponse,
|
||||
buildTimelineProjectContextSummary,
|
||||
} from "../router/timeline-project-context-response-support.js";
|
||||
|
||||
describe("timeline project context response support", () => {
|
||||
it("combines timeline counts with holiday overlay summary data", () => {
|
||||
expect(buildTimelineProjectContextSummary({
|
||||
allocations: [{ projectId: "project_1", resourceId: "resource_1" }],
|
||||
demands: [{ projectId: "project_1" }],
|
||||
assignments: [{ projectId: "project_1", resourceId: "resource_1" }],
|
||||
resourceIds: ["resource_1", "resource_2"],
|
||||
allResourceAllocations: [{ id: "booking_1" }, { id: "booking_2" }],
|
||||
assignmentConflicts: [
|
||||
{ crossProjectOverlapCount: 0 },
|
||||
{ crossProjectOverlapCount: 2 },
|
||||
],
|
||||
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,
|
||||
},
|
||||
{
|
||||
id: "overlay_2",
|
||||
resourceId: "resource_2",
|
||||
startDate: "2026-04-04",
|
||||
endDate: "2026-04-04",
|
||||
note: "Holiday",
|
||||
scope: "CITY",
|
||||
calendarName: "Berlin",
|
||||
sourceType: "CALENDAR",
|
||||
countryCode: "DE",
|
||||
countryName: "Germany",
|
||||
federalState: "BE",
|
||||
metroCityName: "Berlin",
|
||||
},
|
||||
],
|
||||
})).toEqual({
|
||||
allocationCount: 1,
|
||||
demandCount: 1,
|
||||
assignmentCount: 1,
|
||||
projectCount: 1,
|
||||
resourceCount: 1,
|
||||
resourceIds: 2,
|
||||
allResourceAllocationCount: 2,
|
||||
conflictedAssignmentCount: 1,
|
||||
overlayCount: 2,
|
||||
holidayResourceCount: 2,
|
||||
byScope: [
|
||||
{ scope: "CITY", count: 1 },
|
||||
{ scope: "COUNTRY", count: 1 },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("builds anonymized project context responses and detail summaries", () => {
|
||||
const directory = {
|
||||
config: { enabled: true, mode: "alias", domain: "example.test" },
|
||||
byResourceId: new Map([
|
||||
[
|
||||
"resource_1",
|
||||
{
|
||||
displayName: "Anon Alice",
|
||||
eid: "ANON-001",
|
||||
email: "anon-alice@example.test",
|
||||
},
|
||||
],
|
||||
]),
|
||||
byAliasEid: new Map([["anon-001", "resource_1"]]),
|
||||
};
|
||||
|
||||
expect(buildTimelineProjectContextResponse({
|
||||
project: { id: "project_1" },
|
||||
allocations: [
|
||||
{
|
||||
id: "allocation_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Alice", eid: "E-001" },
|
||||
},
|
||||
],
|
||||
demands: [{ id: "demand_1", projectId: "project_1" }],
|
||||
assignments: [
|
||||
{
|
||||
id: "assignment_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Alice", eid: "E-001" },
|
||||
},
|
||||
],
|
||||
allResourceAllocations: [
|
||||
{
|
||||
id: "booking_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Alice", eid: "E-001" },
|
||||
},
|
||||
],
|
||||
resourceIds: ["resource_1"],
|
||||
directory: directory as never,
|
||||
})).toEqual({
|
||||
project: { id: "project_1" },
|
||||
allocations: [
|
||||
{
|
||||
id: "allocation_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Anon Alice", eid: "ANON-001" },
|
||||
},
|
||||
],
|
||||
demands: [{ id: "demand_1", projectId: "project_1" }],
|
||||
assignments: [
|
||||
{
|
||||
id: "assignment_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Anon Alice", eid: "ANON-001" },
|
||||
},
|
||||
],
|
||||
allResourceAllocations: [
|
||||
{
|
||||
id: "booking_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Anon Alice", eid: "ANON-001" },
|
||||
},
|
||||
],
|
||||
resourceIds: ["resource_1"],
|
||||
});
|
||||
|
||||
expect(buildTimelineProjectContextDetailResponse({
|
||||
project: { id: "project_1" },
|
||||
period: {
|
||||
startDate: new Date("2026-04-01T00:00:00.000Z"),
|
||||
endDate: new Date("2026-04-05T00:00:00.000Z"),
|
||||
},
|
||||
allocations: [
|
||||
{
|
||||
id: "allocation_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Alice", eid: "E-001" },
|
||||
},
|
||||
],
|
||||
demands: [{ id: "demand_1", projectId: "project_1" }],
|
||||
assignments: [
|
||||
{
|
||||
id: "assignment_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Alice", eid: "E-001" },
|
||||
},
|
||||
],
|
||||
allResourceAllocations: [
|
||||
{
|
||||
id: "booking_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Alice", eid: "E-001" },
|
||||
},
|
||||
],
|
||||
resourceIds: ["resource_1"],
|
||||
assignmentConflicts: [{ crossProjectOverlapCount: 1, assignmentId: "assignment_1" }],
|
||||
holidayOverlays: [],
|
||||
directory: directory as never,
|
||||
})).toEqual({
|
||||
project: { id: "project_1" },
|
||||
period: {
|
||||
startDate: "2026-04-01",
|
||||
endDate: "2026-04-05",
|
||||
},
|
||||
summary: {
|
||||
allocationCount: 1,
|
||||
demandCount: 1,
|
||||
assignmentCount: 1,
|
||||
projectCount: 1,
|
||||
resourceCount: 1,
|
||||
resourceIds: 1,
|
||||
allResourceAllocationCount: 1,
|
||||
conflictedAssignmentCount: 1,
|
||||
overlayCount: 0,
|
||||
holidayResourceCount: 0,
|
||||
byScope: [],
|
||||
},
|
||||
allocations: [
|
||||
{
|
||||
id: "allocation_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Anon Alice", eid: "ANON-001" },
|
||||
},
|
||||
],
|
||||
demands: [{ id: "demand_1", projectId: "project_1" }],
|
||||
assignments: [
|
||||
{
|
||||
id: "assignment_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Anon Alice", eid: "ANON-001" },
|
||||
},
|
||||
],
|
||||
allResourceAllocations: [
|
||||
{
|
||||
id: "booking_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Anon Alice", eid: "ANON-001" },
|
||||
},
|
||||
],
|
||||
assignmentConflicts: [{ crossProjectOverlapCount: 1, assignmentId: "assignment_1" }],
|
||||
holidayOverlays: [],
|
||||
resourceIds: ["resource_1"],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -14,9 +14,6 @@ vi.mock("../router/timeline-holiday-read.js", async () => {
|
||||
});
|
||||
|
||||
import {
|
||||
buildTimelineProjectContextDetailResponse,
|
||||
buildTimelineProjectContextResponse,
|
||||
buildTimelineProjectContextSummary,
|
||||
loadTimelineProjectContextDetailArtifacts,
|
||||
resolveTimelineProjectContextPeriod,
|
||||
} from "../router/timeline-project-context-support.js";
|
||||
@@ -147,222 +144,6 @@ describe("timeline project context support", () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it("combines timeline counts with holiday overlay summary data", () => {
|
||||
expect(buildTimelineProjectContextSummary({
|
||||
allocations: [{ projectId: "project_1", resourceId: "resource_1" }],
|
||||
demands: [{ projectId: "project_1" }],
|
||||
assignments: [{ projectId: "project_1", resourceId: "resource_1" }],
|
||||
resourceIds: ["resource_1", "resource_2"],
|
||||
allResourceAllocations: [{ id: "booking_1" }, { id: "booking_2" }],
|
||||
assignmentConflicts: [
|
||||
{ crossProjectOverlapCount: 0 },
|
||||
{ crossProjectOverlapCount: 2 },
|
||||
],
|
||||
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,
|
||||
},
|
||||
{
|
||||
id: "overlay_2",
|
||||
resourceId: "resource_2",
|
||||
startDate: "2026-04-04",
|
||||
endDate: "2026-04-04",
|
||||
note: "Holiday",
|
||||
scope: "CITY",
|
||||
calendarName: "Berlin",
|
||||
sourceType: "CALENDAR",
|
||||
countryCode: "DE",
|
||||
countryName: "Germany",
|
||||
federalState: "BE",
|
||||
metroCityName: "Berlin",
|
||||
},
|
||||
],
|
||||
})).toEqual({
|
||||
allocationCount: 1,
|
||||
demandCount: 1,
|
||||
assignmentCount: 1,
|
||||
projectCount: 1,
|
||||
resourceCount: 1,
|
||||
resourceIds: 2,
|
||||
allResourceAllocationCount: 2,
|
||||
conflictedAssignmentCount: 1,
|
||||
overlayCount: 2,
|
||||
holidayResourceCount: 2,
|
||||
byScope: [
|
||||
{ scope: "CITY", count: 1 },
|
||||
{ scope: "COUNTRY", count: 1 },
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("builds anonymized project context responses and detail summaries", () => {
|
||||
const directory = {
|
||||
config: { enabled: true, mode: "alias", domain: "example.test" },
|
||||
byResourceId: new Map([
|
||||
[
|
||||
"resource_1",
|
||||
{
|
||||
displayName: "Anon Alice",
|
||||
eid: "ANON-001",
|
||||
email: "anon-alice@example.test",
|
||||
},
|
||||
],
|
||||
]),
|
||||
byAliasEid: new Map([["anon-001", "resource_1"]]),
|
||||
};
|
||||
|
||||
expect(buildTimelineProjectContextResponse({
|
||||
project: { id: "project_1" },
|
||||
allocations: [
|
||||
{
|
||||
id: "allocation_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Alice", eid: "E-001" },
|
||||
},
|
||||
],
|
||||
demands: [{ id: "demand_1", projectId: "project_1" }],
|
||||
assignments: [
|
||||
{
|
||||
id: "assignment_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Alice", eid: "E-001" },
|
||||
},
|
||||
],
|
||||
allResourceAllocations: [
|
||||
{
|
||||
id: "booking_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Alice", eid: "E-001" },
|
||||
},
|
||||
],
|
||||
resourceIds: ["resource_1"],
|
||||
directory: directory as never,
|
||||
})).toEqual({
|
||||
project: { id: "project_1" },
|
||||
allocations: [
|
||||
{
|
||||
id: "allocation_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Anon Alice", eid: "ANON-001" },
|
||||
},
|
||||
],
|
||||
demands: [{ id: "demand_1", projectId: "project_1" }],
|
||||
assignments: [
|
||||
{
|
||||
id: "assignment_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Anon Alice", eid: "ANON-001" },
|
||||
},
|
||||
],
|
||||
allResourceAllocations: [
|
||||
{
|
||||
id: "booking_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Anon Alice", eid: "ANON-001" },
|
||||
},
|
||||
],
|
||||
resourceIds: ["resource_1"],
|
||||
});
|
||||
|
||||
expect(buildTimelineProjectContextDetailResponse({
|
||||
project: { id: "project_1" },
|
||||
period: {
|
||||
startDate: new Date("2026-04-01T00:00:00.000Z"),
|
||||
endDate: new Date("2026-04-05T00:00:00.000Z"),
|
||||
},
|
||||
allocations: [
|
||||
{
|
||||
id: "allocation_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Alice", eid: "E-001" },
|
||||
},
|
||||
],
|
||||
demands: [{ id: "demand_1", projectId: "project_1" }],
|
||||
assignments: [
|
||||
{
|
||||
id: "assignment_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Alice", eid: "E-001" },
|
||||
},
|
||||
],
|
||||
allResourceAllocations: [
|
||||
{
|
||||
id: "booking_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Alice", eid: "E-001" },
|
||||
},
|
||||
],
|
||||
resourceIds: ["resource_1"],
|
||||
assignmentConflicts: [{ crossProjectOverlapCount: 1, assignmentId: "assignment_1" }],
|
||||
holidayOverlays: [],
|
||||
directory: directory as never,
|
||||
})).toEqual({
|
||||
project: { id: "project_1" },
|
||||
period: {
|
||||
startDate: "2026-04-01",
|
||||
endDate: "2026-04-05",
|
||||
},
|
||||
summary: {
|
||||
allocationCount: 1,
|
||||
demandCount: 1,
|
||||
assignmentCount: 1,
|
||||
projectCount: 1,
|
||||
resourceCount: 1,
|
||||
resourceIds: 1,
|
||||
allResourceAllocationCount: 1,
|
||||
conflictedAssignmentCount: 1,
|
||||
overlayCount: 0,
|
||||
holidayResourceCount: 0,
|
||||
byScope: [],
|
||||
},
|
||||
allocations: [
|
||||
{
|
||||
id: "allocation_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Anon Alice", eid: "ANON-001" },
|
||||
},
|
||||
],
|
||||
demands: [{ id: "demand_1", projectId: "project_1" }],
|
||||
assignments: [
|
||||
{
|
||||
id: "assignment_1",
|
||||
projectId: "project_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Anon Alice", eid: "ANON-001" },
|
||||
},
|
||||
],
|
||||
allResourceAllocations: [
|
||||
{
|
||||
id: "booking_1",
|
||||
resourceId: "resource_1",
|
||||
resource: { id: "resource_1", displayName: "Anon Alice", eid: "ANON-001" },
|
||||
},
|
||||
],
|
||||
assignmentConflicts: [{ crossProjectOverlapCount: 1, assignmentId: "assignment_1" }],
|
||||
holidayOverlays: [],
|
||||
resourceIds: ["resource_1"],
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it("loads detail artifacts with formatted holiday overlays only when resources exist", async () => {
|
||||
loadTimelineHolidayOverlaysMock.mockResolvedValueOnce([
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user