From fa9c8b12b8060d83759d1b9bae6f7dd2e6dcd5f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Tue, 31 Mar 2026 23:47:50 +0200 Subject: [PATCH] test(api): cover assistant dashboard overview reads --- ...assistant-tools-dashboard-overview.test.ts | 77 ++++++++++ ...ant-tools-dashboard-project-health.test.ts | 134 ++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 packages/api/src/__tests__/assistant-tools-dashboard-overview.test.ts create mode 100644 packages/api/src/__tests__/assistant-tools-dashboard-project-health.test.ts diff --git a/packages/api/src/__tests__/assistant-tools-dashboard-overview.test.ts b/packages/api/src/__tests__/assistant-tools-dashboard-overview.test.ts new file mode 100644 index 0000000..38f7ed7 --- /dev/null +++ b/packages/api/src/__tests__/assistant-tools-dashboard-overview.test.ts @@ -0,0 +1,77 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { SystemRole } from "@capakraken/shared"; + +import { + createToolContext, + executeTool, + getDashboardOverview, +} from "./assistant-tools-dashboard-test-helpers.js"; + +describe("assistant dashboard tools overview", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("routes statistics through the dashboard overview path", async () => { + vi.mocked(getDashboardOverview).mockResolvedValue({ + totalResources: 12, + activeResources: 10, + inactiveResources: 2, + totalProjects: 7, + activeProjects: 4, + inactiveProjects: 3, + totalAllocations: 21, + activeAllocations: 18, + cancelledAllocations: 3, + approvedVacations: 6, + totalEstimates: 9, + budgetSummary: { + totalBudgetCents: 1_234_56, + totalCostCents: 654_32, + avgUtilizationPercent: 53, + }, + budgetBasis: { + remainingBudgetCents: 58_024, + budgetedProjects: 5, + unbudgetedProjects: 2, + trackedAssignmentCount: 18, + windowStart: null, + windowEnd: null, + }, + projectsByStatus: [ + { status: "ACTIVE", count: 4 }, + { status: "DRAFT", count: 2 }, + { status: "DONE", count: 1 }, + ], + chapterUtilization: [ + { chapter: "CGI", resourceCount: 5, avgChargeabilityTarget: 78 }, + { chapter: "Compositing", resourceCount: 3, avgChargeabilityTarget: 74 }, + { chapter: "Unassigned", resourceCount: 2, avgChargeabilityTarget: 0 }, + ], + recentActivity: [], + }); + + const ctx = createToolContext({}, { userRole: SystemRole.CONTROLLER }); + const result = await executeTool("get_statistics", "{}", ctx); + + expect(JSON.parse(result.content)).toEqual({ + activeResources: 10, + totalProjects: 7, + activeProjects: 4, + totalAllocations: 21, + approvedVacations: 6, + totalEstimates: 9, + totalBudget: "1.234,56 EUR", + projectsByStatus: { + ACTIVE: 4, + DRAFT: 2, + DONE: 1, + }, + topChapters: [ + { chapter: "CGI", count: 5 }, + { chapter: "Compositing", count: 3 }, + { chapter: "Unassigned", count: 2 }, + ], + }); + }); +}); diff --git a/packages/api/src/__tests__/assistant-tools-dashboard-project-health.test.ts b/packages/api/src/__tests__/assistant-tools-dashboard-project-health.test.ts new file mode 100644 index 0000000..069169a --- /dev/null +++ b/packages/api/src/__tests__/assistant-tools-dashboard-project-health.test.ts @@ -0,0 +1,134 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { SystemRole } from "@capakraken/shared"; + +import { + createToolContext, + executeTool, + getDashboardProjectHealth, +} from "./assistant-tools-dashboard-test-helpers.js"; + +describe("assistant dashboard tools project health", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("routes project health reads through the dashboard router path", async () => { + vi.mocked(getDashboardProjectHealth).mockResolvedValue([ + { + id: "project_1", + projectName: "Apollo", + shortCode: "APL", + status: "ACTIVE", + clientId: "client_1", + clientName: "Acme Mobility", + budgetHealth: 74, + staffingHealth: 88, + timelineHealth: 63, + compositeScore: 75, + budgetCents: 250_000, + spentCents: 180_000, + remainingBudgetCents: 70_000, + budgetUtilizationPercent: 72, + demandHeadcountTotal: 6, + demandHeadcountFilled: 5, + demandHeadcountOpen: 1, + demandRequirementCount: 3, + plannedEndDate: new Date("2026-08-31T00:00:00.000Z"), + daysUntilEndDate: 45, + timelineStatus: "ON_TRACK", + calendarLocations: [ + { + countryCode: "DE", + countryName: "Germany", + federalState: "BY", + metroCityName: "Munich", + assignmentCount: 3, + spentCents: 120_000, + }, + ], + derivation: { + periodStart: "2026-08-01", + periodEnd: "2026-08-31", + calendarContextCount: 1, + holidayAwareAssignmentCount: 3, + fallbackAssignmentCount: 0, + baseSpentCents: 190_000, + adjustedSpentCents: 180_000, + publicHolidayDayEquivalent: 1, + publicHolidayCostDeductionCents: 6_000, + absenceDayEquivalent: 0.5, + absenceCostDeductionCents: 4_000, + }, + }, + ]); + + const ctx = createToolContext({}, { userRole: SystemRole.CONTROLLER }); + const result = await executeTool("get_project_health", "{}", ctx); + + expect(getDashboardProjectHealth).toHaveBeenCalledTimes(1); + expect(JSON.parse(result.content)).toEqual({ + projects: [ + { + projectId: "project_1", + projectName: "Apollo", + shortCode: "APL", + status: "ACTIVE", + overall: 75, + budget: 74, + staffing: 88, + timeline: 63, + rating: "at_risk", + budgetBasis: { + budgetCents: 250_000, + spentCents: 180_000, + remainingBudgetCents: 70_000, + budgetUtilizationPercent: 72, + calendarLocations: [ + { + countryCode: "DE", + countryName: "Germany", + federalState: "BY", + metroCityName: "Munich", + assignmentCount: 3, + spentCents: 120_000, + }, + ], + derivation: { + periodStart: "2026-08-01", + periodEnd: "2026-08-31", + calendarContextCount: 1, + holidayAwareAssignmentCount: 3, + fallbackAssignmentCount: 0, + baseSpentCents: 190_000, + adjustedSpentCents: 180_000, + publicHolidayDayEquivalent: 1, + publicHolidayCostDeductionCents: 6_000, + absenceDayEquivalent: 0.5, + absenceCostDeductionCents: 4_000, + }, + }, + staffingBasis: { + demandHeadcountTotal: 6, + demandHeadcountFilled: 5, + demandHeadcountOpen: 1, + demandRequirementCount: 3, + }, + timelineBasis: { + plannedEndDate: "2026-08-31T00:00:00.000Z", + daysUntilEndDate: 45, + timelineStatus: "ON_TRACK", + }, + context: { + clientId: "client_1", + clientName: "Acme Mobility", + }, + }, + ], + summary: { + healthy: 0, + atRisk: 1, + critical: 0, + }, + }); + }); +});