From 3a82a528976c81cd65999e2684844e673632142f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Wed, 1 Apr 2026 00:31:51 +0200 Subject: [PATCH] test(api): cover assistant allocation reads --- .../assistant-tools-allocation-read.test.ts | 244 ++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 packages/api/src/__tests__/assistant-tools-allocation-read.test.ts diff --git a/packages/api/src/__tests__/assistant-tools-allocation-read.test.ts b/packages/api/src/__tests__/assistant-tools-allocation-read.test.ts new file mode 100644 index 0000000..61e7142 --- /dev/null +++ b/packages/api/src/__tests__/assistant-tools-allocation-read.test.ts @@ -0,0 +1,244 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { PermissionKey, SystemRole } from "@capakraken/shared"; + +vi.mock("@capakraken/application", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + approveEstimateVersion: vi.fn(), + cloneEstimate: vi.fn(), + commitDispoImportBatch: vi.fn(), + countPlanningEntries: vi.fn().mockResolvedValue({ countsByRoleId: new Map() }), + createEstimateExport: vi.fn(), + createEstimatePlanningHandoff: vi.fn(), + createEstimateRevision: vi.fn(), + assessDispoImportReadiness: vi.fn(), + loadResourceDailyAvailabilityContexts: vi.fn().mockResolvedValue(new Map()), + getDashboardDemand: vi.fn().mockResolvedValue([]), + getDashboardBudgetForecast: vi.fn().mockResolvedValue([]), + getDashboardOverview: vi.fn(), + getDashboardSkillGapSummary: vi.fn().mockResolvedValue({ + roleGaps: [], + totalOpenPositions: 0, + skillSupplyTop10: [], + resourcesByRole: [], + }), + getDashboardProjectHealth: vi.fn().mockResolvedValue([]), + getDashboardPeakTimes: vi.fn().mockResolvedValue([]), + getDashboardTopValueResources: vi.fn().mockResolvedValue([]), + getEstimateById: vi.fn(), + listAssignmentBookings: vi.fn().mockResolvedValue([]), + stageDispoImportBatch: vi.fn(), + submitEstimateVersion: vi.fn(), + updateEstimateDraft: vi.fn(), + }; +}); + +import { executeTool } from "../router/assistant-tools.js"; +import { createToolContext } from "./assistant-tools-planning-read-test-helpers.js"; + +describe("assistant planning allocation and vacation read tools", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it("routes allocation listing through the allocation router path", async () => { + const db = { + systemSettings: { + findUnique: vi.fn().mockResolvedValue({ + anonymizationEnabled: false, + anonymizationDomain: null, + anonymizationSeed: null, + anonymizationMode: null, + anonymizationAliases: null, + }), + }, + demandRequirement: { + findMany: vi.fn().mockResolvedValue([ + { + id: "demand_1", + projectId: "project_1", + startDate: new Date("2026-04-01T00:00:00.000Z"), + endDate: new Date("2026-04-30T00:00:00.000Z"), + hoursPerDay: 8, + percentage: 100, + role: "TD", + roleId: null, + headcount: 1, + status: "OPEN", + metadata: null, + createdAt: new Date("2026-03-20T00:00:00.000Z"), + updatedAt: new Date("2026-03-20T00:00:00.000Z"), + project: { + id: "project_1", + name: "Gelddruckmaschine", + shortCode: "GDM", + status: "ACTIVE", + endDate: new Date("2026-04-30T00:00:00.000Z"), + }, + roleEntity: null, + assignments: [], + }, + ]), + }, + assignment: { + findMany: vi.fn().mockResolvedValue([ + { + id: "assign_1", + demandRequirementId: null, + resourceId: "res_1", + projectId: "project_1", + startDate: new Date("2026-04-01T00:00:00.000Z"), + endDate: new Date("2026-04-10T00:00:00.000Z"), + hoursPerDay: 6, + percentage: 75, + role: "Pipeline TD", + roleId: null, + dailyCostCents: 12_000, + status: "ACTIVE", + metadata: null, + createdAt: new Date("2026-03-21T00:00:00.000Z"), + updatedAt: new Date("2026-03-21T00:00:00.000Z"), + resource: { + id: "res_1", + displayName: "Bruce Banner", + eid: "EMP-001", + lcrCents: 8_500, + }, + project: { + id: "project_1", + name: "Gelddruckmaschine", + shortCode: "GDM", + status: "ACTIVE", + endDate: new Date("2026-04-30T00:00:00.000Z"), + }, + roleEntity: null, + demandRequirement: null, + }, + ]), + }, + }; + const ctx = createToolContext(db, { + userRole: SystemRole.CONTROLLER, + permissions: [PermissionKey.VIEW_PLANNING], + }); + + const result = await executeTool( + "list_allocations", + JSON.stringify({ + projectId: "project_1", + resourceName: "Bruce", + projectCode: "GDM", + status: "ACTIVE", + limit: 10, + }), + ctx, + ); + + expect(db.demandRequirement.findMany).toHaveBeenCalledWith({ + where: { + projectId: "project_1", + status: "ACTIVE", + }, + include: { + project: { + select: { + id: true, + name: true, + shortCode: true, + status: true, + endDate: true, + }, + }, + roleEntity: { + select: { id: true, name: true, color: true }, + }, + assignments: { + include: { + resource: { + select: { + id: true, + displayName: true, + eid: true, + lcrCents: true, + chapter: true, + }, + }, + project: { + select: { + id: true, + name: true, + shortCode: true, + status: true, + endDate: true, + }, + }, + roleEntity: { + select: { id: true, name: true, color: true }, + }, + }, + }, + }, + orderBy: { startDate: "asc" }, + }); + expect(db.assignment.findMany).toHaveBeenCalledWith({ + where: { + projectId: "project_1", + status: "ACTIVE", + }, + include: { + resource: { + select: { + id: true, + displayName: true, + eid: true, + lcrCents: true, + chapter: true, + }, + }, + project: { + select: { + id: true, + name: true, + shortCode: true, + status: true, + endDate: true, + }, + }, + roleEntity: { + select: { id: true, name: true, color: true }, + }, + demandRequirement: { + select: { + id: true, + projectId: true, + startDate: true, + endDate: true, + hoursPerDay: true, + percentage: true, + role: true, + roleId: true, + headcount: true, + status: true, + }, + }, + }, + orderBy: { startDate: "asc" }, + }); + expect(JSON.parse(result.content)).toEqual([ + { + id: "assign_1", + resource: "Bruce Banner", + resourceEid: "EMP-001", + project: "Gelddruckmaschine", + projectCode: "GDM", + role: "Pipeline TD", + status: "ACTIVE", + hoursPerDay: 6, + dailyCost: "120,00 EUR", + start: "2026-04-01", + end: "2026-04-10", + }, + ]); + }); +});