import { PermissionKey } from "@capakraken/shared"; import { beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("@capakraken/application", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, getDashboardBudgetForecast: vi.fn().mockResolvedValue([]), listAssignmentBookings: vi.fn().mockResolvedValue([]), }; }); vi.mock("../lib/audit.js", () => ({ createAuditEntry: vi.fn().mockResolvedValue(undefined), })); import { executeTool } from "../router/assistant-tools.js"; import { createToolContext } from "./assistant-tools-holiday-capacity-test-helpers.js"; describe("assistant holiday-aware chargeability tool", () => { beforeEach(() => { vi.clearAllMocks(); }); it("calculates chargeability with regional holidays excluded from booked and available hours", async () => { const resourceRecord = { id: "res_1", displayName: "Bruce Banner", eid: "bruce.banner", fte: 1, lcrCents: 5000, chargeabilityTarget: 80, availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8, saturday: 0, sunday: 0 }, countryId: "country_de", federalState: "BY", metroCityId: null, country: { id: "country_de", code: "DE", name: "Deutschland", dailyWorkingHours: 8, scheduleRules: null }, metroCity: null, managementLevelGroup: null, }; const db = { resource: { findUnique: vi.fn().mockResolvedValueOnce(resourceRecord), findUniqueOrThrow: vi.fn().mockResolvedValue(resourceRecord), findFirst: vi.fn(), }, assignment: { findMany: vi.fn().mockResolvedValue([ { id: "assign_1", hoursPerDay: 8, startDate: new Date("2026-01-05T00:00:00.000Z"), endDate: new Date("2026-01-06T00:00:00.000Z"), dailyCostCents: 40000, status: "CONFIRMED", project: { id: "project_gamma", name: "Gamma", shortCode: "GAM", budgetCents: null, winProbability: 100, utilizationCategory: { code: "Chg" }, }, }, ]), }, vacation: { findMany: vi.fn().mockResolvedValue([]), }, holidayCalendar: { findMany: vi.fn().mockResolvedValue([]), }, calculationRule: { findMany: vi.fn().mockResolvedValue([]), }, }; const ctx = createToolContext(db, [PermissionKey.VIEW_COSTS]); const result = await executeTool( "get_chargeability", JSON.stringify({ resourceId: "res_1", month: "2026-01" }), ctx, ); const parsed = JSON.parse(result.content) as { baseWorkingDays: number; baseAvailableHours: number; availableHours: number; bookedHours: number; workingDays: number; targetHours: number; unassignedHours: number; holidaySummary: { count: number; workdayCount: number; hoursDeduction: number }; capacityBreakdown: { formula: string; holidayHoursDeduction: number; absenceHoursDeduction: number }; locationContext: { federalState: string | null }; allocations: Array<{ hours: number }>; }; expect(parsed.bookedHours).toBe(8); expect(parsed.allocations).toEqual([expect.objectContaining({ hours: 8 })]); expect(parsed.baseWorkingDays).toBe(22); expect(parsed.baseAvailableHours).toBe(176); expect(parsed.availableHours).toBe(160); expect(parsed.workingDays).toBe(20); expect(parsed.targetHours).toBe(128); expect(parsed.unassignedHours).toBe(152); expect(parsed.locationContext.federalState).toBe("BY"); expect(parsed.holidaySummary).toEqual( expect.objectContaining({ count: 2, workdayCount: 2, hoursDeduction: 16, }), ); expect(parsed.capacityBreakdown).toEqual( expect.objectContaining({ formula: "baseAvailableHours - holidayHoursDeduction - absenceHoursDeduction = availableHours", holidayHoursDeduction: 16, absenceHoursDeduction: 0, }), ); }); });