import { describe, expect, it, vi } from "vitest"; import { SystemRole } from "@capakraken/shared"; 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, type ToolContext } from "../router/assistant-tools.js"; function createToolContext( db: Record, permissions: string[] = [], userRole: SystemRole = SystemRole.ADMIN, ): ToolContext { return { db: db as ToolContext["db"], userId: "user_1", userRole, permissions: new Set(permissions) as ToolContext["permissions"], session: { user: { email: "assistant@example.com", name: "Assistant User", image: null }, expires: "2026-03-29T00:00:00.000Z", }, dbUser: { id: "user_1", systemRole: userRole, permissionOverrides: null, }, roleDefaults: null, }; } describe("assistant holiday tools", () => { it("lists regional holidays and distinguishes Bavaria from Hamburg", async () => { const ctx = createToolContext({}); const bavaria = await executeTool( "list_holidays_by_region", JSON.stringify({ countryCode: "DE", federalState: "BY", year: 2026 }), ctx, ); const hamburg = await executeTool( "list_holidays_by_region", JSON.stringify({ countryCode: "DE", federalState: "HH", year: 2026 }), ctx, ); const bavariaResult = JSON.parse(bavaria.content) as { count: number; locationContext: { federalState: string | null }; summary: { byScope: Array<{ scope: string; count: number }> }; holidays: Array<{ name: string; date: string }>; }; const hamburgResult = JSON.parse(hamburg.content) as { count: number; locationContext: { federalState: string | null }; holidays: Array<{ name: string; date: string }>; }; expect(bavariaResult.count).toBeGreaterThan(hamburgResult.count); expect(bavariaResult.locationContext.federalState).toBe("BY"); expect(bavariaResult.summary.byScope).toEqual( expect.arrayContaining([expect.objectContaining({ scope: "STATE" })]), ); expect(bavariaResult.holidays).toEqual( expect.arrayContaining([ expect.objectContaining({ name: "Heilige Drei Könige", date: "2026-01-06" }), ]), ); expect(hamburgResult.holidays).not.toEqual( expect.arrayContaining([ expect.objectContaining({ name: "Heilige Drei Könige", date: "2026-01-06" }), ]), ); }); it("resolves resource-specific holidays including city-local dates", async () => { const db = { resource: { findUnique: vi .fn() .mockResolvedValueOnce(null) .mockResolvedValueOnce({ id: "res_1", eid: "bruce.banner", displayName: "Bruce Banner" }) .mockResolvedValueOnce({ id: "res_1", eid: "bruce.banner", displayName: "Bruce Banner", federalState: "BY", countryId: "country_de", metroCityId: "city_augsburg", country: { code: "DE", name: "Deutschland" }, metroCity: { name: "Augsburg" } }), findFirst: vi.fn(), }, }; const ctx = createToolContext(db); const result = await executeTool( "get_resource_holidays", JSON.stringify({ identifier: "bruce.banner", year: 2026 }), ctx, ); const parsed = JSON.parse(result.content) as { resource: { eid: string; federalState: string | null; metroCity: string | null }; summary: { byScope: Array<{ scope: string; count: number }> }; holidays: Array<{ name: string; date: string }>; }; expect(parsed.resource).toEqual( expect.objectContaining({ eid: "bruce.banner", federalState: "BY", metroCity: "Augsburg", }), ); expect(parsed.holidays).toEqual( expect.arrayContaining([ expect.objectContaining({ name: "Augsburger Friedensfest", date: "2026-08-08" }), ]), ); expect(parsed.summary.byScope).toEqual( expect.arrayContaining([expect.objectContaining({ scope: "CITY" })]), ); }); it("lists holiday calendars with scope metadata and entry counts", async () => { const ctx = createToolContext({ holidayCalendar: { findMany: vi.fn().mockResolvedValue([ { id: "cal_by", name: "Bayern Feiertage", scopeType: "STATE", stateCode: "BY", isActive: true, priority: 10, country: { id: "country_de", code: "DE", name: "Deutschland" }, metroCity: null, _count: { entries: 2 }, entries: [ { id: "entry_1", date: new Date("2026-01-06T00:00:00.000Z"), name: "Heilige Drei Koenige", isRecurringAnnual: true, source: "state", }, ], }, ]), }, }); const result = await executeTool( "list_holiday_calendars", JSON.stringify({ countryCode: "DE", scopeType: "STATE", includeInactive: true }), ctx, ); const parsed = JSON.parse(result.content) as { count: number; calendars: Array<{ name: string; scopeType: string; stateCode: string | null; entryCount: number; country: { code: string }; }>; }; expect(parsed.count).toBe(1); expect(parsed.calendars).toHaveLength(1); expect(parsed.calendars[0]).toMatchObject({ name: "Bayern Feiertage", scopeType: "STATE", stateCode: "BY", entryCount: 2, country: { code: "DE" }, }); }); it("previews resolved holiday calendars for a scope and shows the source calendar", async () => { const ctx = createToolContext({ country: { findUnique: vi.fn().mockResolvedValue({ id: "country_de", code: "DE", name: "Deutschland" }), }, metroCity: { findUnique: vi.fn().mockResolvedValue({ id: "city_augsburg", name: "Augsburg", countryId: "country_de" }), }, holidayCalendar: { findMany: vi.fn().mockResolvedValue([ { id: "cal_city", name: "Augsburg lokal", scopeType: "CITY", priority: 5, createdAt: new Date("2026-01-01T00:00:00.000Z"), entries: [ { id: "entry_1", date: new Date("2020-08-08T00:00:00.000Z"), name: "Friedensfest lokal", isRecurringAnnual: true, source: "manual", }, ], }, ]), }, }); const result = await executeTool( "preview_resolved_holiday_calendar", JSON.stringify({ countryId: "country_de", metroCityId: "city_augsburg", year: 2026 }), ctx, ); const parsed = JSON.parse(result.content) as { count: number; locationContext: { countryCode: string; metroCity: string | null; year: number }; holidays: Array<{ name: string; calendarName: string; scope: string; date: string }>; }; expect(parsed.count).toBeGreaterThan(0); expect(parsed.locationContext).toEqual( expect.objectContaining({ countryCode: "DE", metroCity: "Augsburg", year: 2026, }), ); expect(parsed.holidays).toEqual( expect.arrayContaining([ expect.objectContaining({ name: "Friedensfest lokal", calendarName: "Augsburg lokal", scope: "CITY", date: "2026-08-08", }), ]), ); expect(ctx.db.country.findUnique).toHaveBeenCalledWith({ where: { id: "country_de" }, select: { id: true, code: true, name: true }, }); expect(ctx.db.metroCity.findUnique).toHaveBeenCalledWith({ where: { id: "city_augsburg" }, select: { id: true, name: true, countryId: true }, }); }); it("creates a holiday calendar through the assistant for admin users", async () => { const ctx = createToolContext({ country: { findUnique: vi.fn().mockResolvedValue({ id: "country_de", code: "DE", name: "Deutschland" }), }, holidayCalendar: { findFirst: vi.fn().mockResolvedValue(null), create: vi.fn().mockResolvedValue({ id: "cal_by", name: "Bayern Feiertage", scopeType: "STATE", stateCode: "BY", isActive: true, priority: 10, country: { id: "country_de", code: "DE", name: "Deutschland" }, metroCity: null, entries: [], }), }, }); const result = await executeTool( "create_holiday_calendar", JSON.stringify({ name: "Bayern Feiertage", scopeType: "STATE", countryId: "country_de", stateCode: "BY", priority: 10, }), ctx, ); const parsed = JSON.parse(result.content) as { success: boolean; message: string; calendar: { name: string; stateCode: string | null }; }; expect(parsed.success).toBe(true); expect(parsed.message).toContain("Created holiday calendar"); expect(parsed.calendar).toEqual( expect.objectContaining({ name: "Bayern Feiertage", stateCode: "BY", }), ); }); it("rejects holiday calendar mutations for non-admin assistant users", async () => { const ctx = createToolContext({}, [], SystemRole.MANAGER); const result = await executeTool( "create_holiday_calendar", JSON.stringify({ name: "Hamburg Feiertage", scopeType: "STATE", countryId: "country_de", stateCode: "HH", }), ctx, ); expect(JSON.parse(result.content)).toEqual( expect.objectContaining({ error: "Admin role required to perform this action.", }), ); }); it("returns stable assistant errors for holiday calendar and entry mutations", async () => { const cases = [ { name: "invalid holiday calendar scope", toolName: "create_holiday_calendar", db: { country: { findUnique: vi.fn().mockResolvedValue({ id: "country_de", code: "DE", name: "Deutschland" }), }, holidayCalendar: { findFirst: vi.fn().mockResolvedValue(null), }, }, payload: { name: "Ungueltiger Kalender", scopeType: "STATE", countryId: "country_de", }, expected: "Holiday calendar scope is invalid.", }, { name: "duplicate holiday calendar scope", toolName: "create_holiday_calendar", db: { country: { findUnique: vi.fn().mockResolvedValue({ id: "country_de", code: "DE", name: "Deutschland" }), }, holidayCalendar: { findFirst: vi.fn().mockResolvedValue({ id: "cal_existing" }), }, }, payload: { name: "Bayern Feiertage", scopeType: "STATE", countryId: "country_de", stateCode: "BY", }, expected: "A holiday calendar for this scope already exists.", }, { name: "holiday calendar not found on delete", toolName: "delete_holiday_calendar", db: { holidayCalendar: { findUnique: vi.fn().mockResolvedValue(null), }, }, payload: { id: "cal_missing" }, expected: "Holiday calendar not found with the given criteria.", }, { name: "holiday calendar entry not found on delete", toolName: "delete_holiday_calendar_entry", db: { holidayCalendarEntry: { findUnique: vi.fn().mockResolvedValue(null), }, }, payload: { id: "entry_missing" }, expected: "Holiday calendar entry not found with the given criteria.", }, { name: "duplicate holiday calendar entry date", toolName: "create_holiday_calendar_entry", db: { holidayCalendar: { findUnique: vi.fn().mockResolvedValue({ id: "cal_by", name: "Bayern Feiertage" }), }, holidayCalendarEntry: { findFirst: vi.fn().mockResolvedValue({ id: "entry_existing" }), }, }, payload: { holidayCalendarId: "cal_by", date: "2026-01-06", name: "Heilige Drei Koenige", }, expected: "A holiday entry for this calendar and date already exists.", }, ] as const; for (const testCase of cases) { const result = await executeTool( testCase.toolName, JSON.stringify(testCase.payload), createToolContext(testCase.db), ); expect(JSON.parse(result.content)).toEqual({ error: testCase.expected, }); } }); 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); 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, }), ); }); it("returns holiday-aware budget forecast data from the dashboard use-case", async () => { const { getDashboardBudgetForecast } = await import("@capakraken/application"); vi.mocked(getDashboardBudgetForecast).mockResolvedValue([ { projectId: "project_1", projectName: "Gelddruckmaschine", shortCode: "GDM", budgetCents: 100_000, spentCents: 60_000, burnRate: 5_000, pctUsed: 60, estimatedExhaustionDate: "2026-02-20", }, ]); const ctx = createToolContext({}, ["viewCosts"]); const result = await executeTool("get_budget_forecast", "{}", ctx); const parsed = JSON.parse(result.content) as { forecasts: Array<{ projectName: string; shortCode: string; budgetCents: number; spentCents: number; remainingCents: number; projectedCents: number; burnRateCents: number; burnStatus: string; }>; }; expect(parsed.forecasts).toEqual([ expect.objectContaining({ projectName: "Gelddruckmaschine", shortCode: "GDM", budgetCents: 100_000, spentCents: 60_000, remainingCents: 40_000, projectedCents: 100_000, burnRateCents: 5_000, burnStatus: "on_track", }), ]); }); it("checks resource availability with regional holidays excluded from capacity", async () => { const resourceRecord = { id: "res_1", displayName: "Bruce Banner", eid: "bruce.banner", fte: 1, availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8, saturday: 0, sunday: 0 }, countryId: "country_de", federalState: "BY", metroCityId: null, country: { code: "DE", dailyWorkingHours: 8 }, metroCity: null, }; const db = { resource: { findUnique: vi .fn() .mockResolvedValue(resourceRecord), findFirst: vi.fn(), }, assignment: { findMany: vi.fn().mockResolvedValue([ { hoursPerDay: 8, startDate: new Date("2026-01-05T00:00:00.000Z"), endDate: new Date("2026-01-06T00:00:00.000Z"), status: "CONFIRMED", project: { name: "Gamma", shortCode: "GAM" }, }, ]), }, vacation: { findMany: vi.fn().mockResolvedValue([]), }, }; const ctx = createToolContext(db); const result = await executeTool( "check_resource_availability", JSON.stringify({ resourceId: "res_1", startDate: "2026-01-05", endDate: "2026-01-06" }), ctx, ); const parsed = JSON.parse(result.content) as { workingDays: number; periodAvailableHours: number; periodBookedHours: number; periodRemainingHours: number; availableHoursPerDay: number; isFullyAvailable: boolean; }; expect(parsed.workingDays).toBe(1); expect(parsed.periodAvailableHours).toBe(8); expect(parsed.periodBookedHours).toBe(8); expect(parsed.periodRemainingHours).toBe(0); expect(parsed.availableHoursPerDay).toBe(0); expect(parsed.isFullyAvailable).toBe(false); }); it("keeps scenario simulation flat when a proposed change falls on a local holiday", async () => { const db = { project: { findUnique: vi.fn().mockResolvedValue({ id: "project_1", name: "Holiday Project", budgetCents: 500_000, startDate: new Date("2026-01-01T00:00:00.000Z"), endDate: new Date("2026-01-31T00:00:00.000Z"), }), }, assignment: { findMany: vi.fn().mockResolvedValue([ { id: "assignment_1", resourceId: "res_1", hoursPerDay: 8, startDate: new Date("2026-01-05T00:00:00.000Z"), endDate: new Date("2026-01-05T00:00:00.000Z"), status: "CONFIRMED", resource: { id: "res_1", displayName: "Bruce Banner", lcrCents: 100, availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 }, chargeabilityTarget: 80, countryId: "country_de", federalState: "BY", metroCityId: null, country: { code: "DE", dailyWorkingHours: 8 }, metroCity: null, }, }, ]), }, resource: { findMany: vi.fn().mockResolvedValue([ { id: "res_1", displayName: "Bruce Banner", lcrCents: 100, availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 }, chargeabilityTarget: 80, countryId: "country_de", federalState: "BY", metroCityId: null, country: { code: "DE", dailyWorkingHours: 8 }, metroCity: null, }, ]), }, }; const ctx = createToolContext(db, ["manageAllocations"]); const result = await executeTool( "simulate_scenario", JSON.stringify({ projectId: "project_1", changes: [ { resourceId: "res_1", startDate: "2026-01-06", endDate: "2026-01-06", hoursPerDay: 8, }, ], }), ctx, ); const parsed = JSON.parse(result.content) as { baseline: { totalHours: number; totalCostCents: number }; scenario: { totalHours: number; totalCostCents: number }; delta: { hours: number; costCents: number }; }; expect(parsed.baseline).toEqual( expect.objectContaining({ totalHours: 8, totalCostCents: 800, }), ); expect(parsed.scenario).toEqual( expect.objectContaining({ totalHours: 8, totalCostCents: 800, }), ); expect(parsed.delta).toEqual( expect.objectContaining({ hours: 0, costCents: 0, }), ); }); it("prefers resources without a local holiday in staffing suggestions", async () => { const db = { project: { findUnique: vi.fn().mockResolvedValue({ id: "project_1", name: "Holiday Project", shortCode: "HP", startDate: new Date("2026-01-06T00:00:00.000Z"), endDate: new Date("2026-01-06T00:00:00.000Z"), }), findFirst: vi.fn().mockResolvedValue(null), }, assignment: { findMany: vi.fn().mockResolvedValue([]), }, resource: { findMany: vi.fn().mockResolvedValue([ { id: "res_by", displayName: "Bavaria", eid: "BY-1", fte: 1, lcrCents: 10000, chargeabilityTarget: 80, valueScore: 10, skills: [], availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 }, countryId: "country_de", federalState: "BY", metroCityId: null, country: { code: "DE", name: "Deutschland" }, metroCity: null, areaRole: { name: "Consultant" }, chapter: "CGI", }, { id: "res_hh", displayName: "Hamburg", eid: "HH-1", fte: 1, lcrCents: 10000, chargeabilityTarget: 80, valueScore: 10, skills: [], availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 }, countryId: "country_de", federalState: "HH", metroCityId: null, country: { code: "DE", name: "Deutschland" }, metroCity: null, areaRole: { name: "Consultant" }, chapter: "CGI", }, ]), }, }; const ctx = createToolContext(db); const result = await executeTool( "get_staffing_suggestions", JSON.stringify({ projectId: "project_1", limit: 5 }), ctx, ); const parsed = JSON.parse(result.content) as { suggestions: Array<{ name: string; availableHours: number }>; }; expect(parsed.suggestions).toHaveLength(1); expect(parsed.suggestions[0]).toEqual( expect.objectContaining({ name: "Hamburg", availableHours: 8 }), ); expect(db.project.findUnique).toHaveBeenCalledWith({ where: { id: "project_1" }, select: expect.objectContaining({ id: true, shortCode: true, name: true, startDate: true, endDate: true, }), }); }); it("finds capacity with local holidays respected", async () => { const db = { resource: { findMany: vi.fn().mockResolvedValue([ { id: "res_by", displayName: "Bavaria", eid: "BY-1", fte: 1, availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 }, countryId: "country_de", federalState: "BY", metroCityId: null, country: { code: "DE" }, metroCity: null, areaRole: { name: "Consultant" }, chapter: "CGI", assignments: [], }, { id: "res_hh", displayName: "Hamburg", eid: "HH-1", fte: 1, availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 }, countryId: "country_de", federalState: "HH", metroCityId: null, country: { code: "DE" }, metroCity: null, areaRole: { name: "Consultant" }, chapter: "CGI", assignments: [], }, ]), }, vacation: { findMany: vi.fn().mockResolvedValue([]), }, }; const ctx = createToolContext(db); const result = await executeTool( "find_capacity", JSON.stringify({ startDate: "2026-01-06", endDate: "2026-01-06", minHoursPerDay: 1 }), ctx, ); const parsed = JSON.parse(result.content) as { results: Array<{ name: string; availableHours: number; availableHoursPerDay: number }>; }; expect(parsed.results).toHaveLength(1); expect(parsed.results[0]).toEqual( expect.objectContaining({ name: "Hamburg", availableHours: 8, availableHoursPerDay: 8 }), ); }); it("returns a stable assistant error when staffing suggestions receive an invalid optional start date", async () => { const db = { project: { findUnique: vi .fn() .mockResolvedValueOnce(null) .mockResolvedValueOnce({ id: "project_1", name: "Holiday Project", shortCode: "HP", status: "ACTIVE", responsiblePerson: null, startDate: new Date("2026-01-05T00:00:00.000Z"), endDate: new Date("2026-01-16T00:00:00.000Z"), }), findFirst: vi.fn().mockResolvedValue(null), }, }; const ctx = createToolContext(db); const result = await executeTool( "get_staffing_suggestions", JSON.stringify({ projectId: "project_1", startDate: "2026-99-01" }), ctx, ); expect(JSON.parse(result.content)).toEqual({ error: "Invalid startDate: 2026-99-01", }); }); it("uses holiday-aware assignment hours for assistant shoring ratio", async () => { const db = { project: { findUnique: vi .fn() .mockResolvedValueOnce({ id: "project_1", name: "Holiday Project", shortCode: "HP", status: "ACTIVE", responsiblePerson: null, }) .mockResolvedValueOnce({ id: "project_1", name: "Holiday Project", shoringThreshold: 55, onshoreCountryCode: "DE", }), }, assignment: { findMany: vi.fn().mockResolvedValue([ { resourceId: "res_by", hoursPerDay: 8, startDate: new Date("2026-01-06T00:00:00.000Z"), endDate: new Date("2026-01-06T00:00:00.000Z"), resource: { id: "res_by", availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 }, countryId: "country_de", federalState: "BY", metroCityId: null, country: { code: "DE" }, metroCity: null, }, }, { resourceId: "res_in", hoursPerDay: 8, startDate: new Date("2026-01-06T00:00:00.000Z"), endDate: new Date("2026-01-06T00:00:00.000Z"), resource: { id: "res_in", availability: { monday: 8, tuesday: 8, wednesday: 8, thursday: 8, friday: 8 }, countryId: "country_in", federalState: null, metroCityId: null, country: { code: "IN" }, metroCity: null, }, }, ]), }, vacation: { findMany: vi.fn().mockResolvedValue([]), }, }; const ctx = createToolContext(db); const result = await executeTool( "get_shoring_ratio", JSON.stringify({ projectId: "project_1" }), ctx, ); expect(result.content).toContain("0% onshore (DE), 100% offshore"); expect(result.content).toContain("IN 100% (1 people)"); }); it("routes pending vacation approvals through the vacation router path", async () => { const db = { vacation: { findMany: vi.fn().mockResolvedValue([ { id: "vac_1", type: "ANNUAL", startDate: new Date("2026-07-01T00:00:00.000Z"), endDate: new Date("2026-07-03T00:00:00.000Z"), isHalfDay: false, resource: { displayName: "Bruce Banner", eid: "BB-1", chapter: "CGI" }, requestedBy: { id: "user_2", name: "Manager", email: "manager@example.com" }, }, ]), }, }; const ctx = createToolContext(db, [], SystemRole.MANAGER); const result = await executeTool( "get_pending_vacation_approvals", JSON.stringify({ limit: 10 }), ctx, ); expect(db.vacation.findMany).toHaveBeenCalledWith({ where: { status: "PENDING" }, include: { resource: { select: expect.any(Object) }, requestedBy: { select: { id: true, name: true, email: true } }, }, orderBy: { startDate: "asc" }, }); expect(JSON.parse(result.content)).toEqual([ expect.objectContaining({ id: "vac_1", resource: "Bruce Banner", eid: "BB-1", chapter: "CGI", }), ]); }); it("routes team vacation overlap through the vacation router path", async () => { const db = { resource: { findUnique: vi .fn() .mockResolvedValue({ id: "res_1", displayName: "Bruce Banner", eid: "BB-1", chapter: "CGI", lcrCents: 0, isActive: true, countryId: null, federalState: null, metroCityId: null, areaRole: null, country: null, metroCity: null, }), findFirst: vi.fn(), findMany: vi.fn(), }, vacation: { findMany: vi.fn().mockResolvedValue([ { type: "ANNUAL", status: "APPROVED", startDate: new Date("2026-08-10T00:00:00.000Z"), endDate: new Date("2026-08-12T00:00:00.000Z"), resource: { displayName: "Clark Kent" }, }, ]), }, }; const ctx = createToolContext(db); const result = await executeTool( "get_team_vacation_overlap", JSON.stringify({ resourceId: "res_1", startDate: "2026-08-10", endDate: "2026-08-12", }), ctx, ); expect(db.vacation.findMany).toHaveBeenCalledWith({ where: { resource: { chapter: "CGI" }, resourceId: { not: "res_1" }, status: { in: ["APPROVED", "PENDING"] }, startDate: { lte: new Date("2026-08-12T00:00:00.000Z") }, endDate: { gte: new Date("2026-08-10T00:00:00.000Z") }, }, include: { resource: { select: expect.any(Object) }, }, orderBy: { startDate: "asc" }, take: 20, }); expect(JSON.parse(result.content)).toEqual( expect.objectContaining({ resource: "Bruce Banner", chapter: "CGI", overlapCount: 1, overlappingVacations: [ expect.objectContaining({ resource: "Clark Kent", status: "APPROVED", }), ], }), ); }); });