import { SystemRole } from "@capakraken/shared"; import { describe, expect, it, vi } from "vitest"; import { createCallerFactory } from "../trpc.js"; import { holidayCalendarRouter } from "../router/holiday-calendar.js"; vi.mock("../lib/audit.js", () => ({ createAuditEntry: vi.fn().mockResolvedValue(undefined), })); const createCaller = createCallerFactory(holidayCalendarRouter); function createProtectedCaller(db: Record) { return createCaller({ session: { user: { email: "user@example.com", name: "User", image: null }, expires: "2026-12-31T00:00:00.000Z", }, db: db as never, dbUser: { id: "user_1", systemRole: SystemRole.USER, permissionOverrides: null, }, }); } function createAdminCaller(db: Record) { return createCaller({ session: { user: { email: "admin@example.com", name: "Admin", image: null }, expires: "2026-12-31T00:00:00.000Z", }, db: db as never, dbUser: { id: "admin_1", systemRole: SystemRole.ADMIN, permissionOverrides: null, }, }); } describe("holiday calendar router", () => { it("requires admin access for holiday calendar catalog reads", async () => { const findMany = vi.fn(); const findUnique = vi.fn(); const findFirst = vi.fn(); const caller = createProtectedCaller({ holidayCalendar: { findMany, findUnique, findFirst, }, }); await expect(caller.listCalendars({ includeInactive: true })).rejects.toMatchObject({ code: "FORBIDDEN", message: "Admin role required", }); await expect(caller.listCalendarsDetail({ includeInactive: true })).rejects.toMatchObject({ code: "FORBIDDEN", message: "Admin role required", }); await expect(caller.getCalendarByIdentifier({ identifier: "Deutschland" })).rejects.toMatchObject({ code: "FORBIDDEN", message: "Admin role required", }); await expect(caller.getCalendarByIdentifierDetail({ identifier: "Deutschland" })).rejects.toMatchObject({ code: "FORBIDDEN", message: "Admin role required", }); await expect(caller.getCalendarById({ id: "cal_de" })).rejects.toMatchObject({ code: "FORBIDDEN", message: "Admin role required", }); expect(findMany).not.toHaveBeenCalled(); expect(findUnique).not.toHaveBeenCalled(); expect(findFirst).not.toHaveBeenCalled(); }); it("allows admins to read holiday calendar catalog routes", async () => { const listRows = [ { id: "cal_de", name: "Deutschland", scopeType: "COUNTRY", stateCode: null, isActive: true, priority: 0, country: { id: "country_de", code: "DE", name: "Deutschland" }, metroCity: null, _count: { entries: 1 }, entries: [ { id: "entry_1", date: new Date("2026-01-01T00:00:00.000Z"), name: "Neujahr", isRecurringAnnual: true, source: "builtin", }, ], }, ]; const detailRow = { id: "cal_de", name: "Deutschland", scopeType: "COUNTRY", stateCode: null, isActive: true, priority: 0, country: { id: "country_de", code: "DE", name: "Deutschland" }, metroCity: null, entries: [ { id: "entry_1", date: new Date("2026-01-01T00:00:00.000Z"), name: "Neujahr", isRecurringAnnual: true, source: "builtin", }, ], }; const findMany = vi.fn().mockResolvedValue(listRows); const findUnique = vi .fn() .mockResolvedValueOnce(detailRow) .mockResolvedValueOnce(detailRow) .mockResolvedValueOnce(detailRow); const findFirst = vi.fn(); const caller = createAdminCaller({ holidayCalendar: { findMany, findUnique, findFirst, }, }); const listResult = await caller.listCalendars({ includeInactive: true }); const detailResult = await caller.listCalendarsDetail({ includeInactive: true }); const byIdentifierResult = await caller.getCalendarByIdentifier({ identifier: "cal_de" }); const byIdentifierDetailResult = await caller.getCalendarByIdentifierDetail({ identifier: "cal_de" }); const byIdResult = await caller.getCalendarById({ id: "cal_de" }); expect(listResult).toEqual(listRows); expect(detailResult).toEqual({ count: 1, calendars: [ expect.objectContaining({ id: "cal_de", entryCount: 1, }), ], }); expect(byIdentifierResult).toEqual(detailRow); expect(byIdentifierDetailResult).toEqual( expect.objectContaining({ id: "cal_de", entryCount: 1, }), ); expect(byIdResult).toEqual(detailRow); expect(findMany).toHaveBeenCalledTimes(2); expect(findUnique).toHaveBeenCalledTimes(3); expect(findFirst).not.toHaveBeenCalled(); }); it("lists holiday calendars with assistant-facing detail formatting", async () => { const db = { 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 caller = createAdminCaller(db); const result = await caller.listCalendarsDetail({ countryCode: "DE", scopeType: "STATE", includeInactive: true, }); expect(result).toEqual({ count: 1, calendars: [ expect.objectContaining({ id: "cal_by", name: "Bayern Feiertage", scopeType: "STATE", stateCode: "BY", entryCount: 2, country: { id: "country_de", code: "DE", name: "Deutschland" }, entries: [ expect.objectContaining({ id: "entry_1", date: "2026-01-06", name: "Heilige Drei Koenige", isRecurringAnnual: true, source: "state", }), ], }), ], }); }); it("resolves a holiday calendar by identifier with assistant-facing detail formatting", async () => { const db = { holidayCalendar: { findUnique: vi.fn().mockResolvedValue(null), findFirst: vi .fn() .mockResolvedValueOnce({ id: "cal_augsburg", name: "Augsburg lokal", scopeType: "CITY", stateCode: null, isActive: true, priority: 5, country: { id: "country_de", code: "DE", name: "Deutschland" }, metroCity: { id: "city_augsburg", name: "Augsburg" }, entries: [ { id: "entry_1", date: new Date("2026-08-08T00:00:00.000Z"), name: "Friedensfest lokal", isRecurringAnnual: true, source: "manual", }, ], }), }, }; const caller = createAdminCaller(db); const result = await caller.getCalendarByIdentifierDetail({ identifier: "Augsburg lokal" }); expect(result).toEqual( expect.objectContaining({ id: "cal_augsburg", name: "Augsburg lokal", scopeType: "CITY", entryCount: 1, metroCity: { id: "city_augsburg", name: "Augsburg" }, entries: [ expect.objectContaining({ id: "entry_1", date: "2026-08-08", name: "Friedensfest lokal", }), ], }), ); }); it("merges built-in and scoped custom holidays in preview", async () => { const db = { country: { findUnique: vi.fn().mockResolvedValue({ code: "DE" }), }, metroCity: { findUnique: vi.fn().mockResolvedValue({ name: "Augsburg" }), }, holidayCalendar: { findMany: vi.fn().mockResolvedValue([ { id: "cal_city", name: "Augsburg lokal", scopeType: "CITY", priority: 10, createdAt: new Date("2026-01-01T00:00:00.000Z"), entries: [ { date: new Date("2020-01-01T00:00:00.000Z"), name: "Augsburg Neujahr", isRecurringAnnual: true, }, { date: new Date("2020-08-08T00:00:00.000Z"), name: "Friedensfest lokal", isRecurringAnnual: true, }, ], }, ]), }, }; const caller = createProtectedCaller(db); const result = await caller.previewResolvedHolidays({ countryId: "country_de", metroCityId: "city_augsburg", year: 2026, }); expect(db.holidayCalendar.findMany).toHaveBeenCalledWith( expect.objectContaining({ where: expect.objectContaining({ countryId: "country_de", isActive: true, }), }), ); expect(result).toEqual( expect.arrayContaining([ expect.objectContaining({ date: "2026-01-01", name: "Augsburg Neujahr", scopeType: "CITY", calendarName: "Augsburg lokal", }), expect.objectContaining({ date: "2026-08-08", name: "Friedensfest lokal", scopeType: "CITY", calendarName: "Augsburg lokal", }), ]), ); }); it("formats preview results for assistant consumption", async () => { const db = { 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: 10, 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 caller = createProtectedCaller(db); const result = await caller.previewResolvedHolidaysDetail({ countryId: "country_de", metroCityId: "city_augsburg", year: 2026, }); expect(result.locationContext).toEqual({ countryId: "country_de", countryCode: "DE", stateCode: null, metroCityId: "city_augsburg", metroCity: "Augsburg", year: 2026, }); expect(result.summary.byScope).toEqual( expect.arrayContaining([expect.objectContaining({ scope: "CITY" })]), ); expect(result.holidays).toEqual( expect.arrayContaining([ expect.objectContaining({ date: "2026-08-08", name: "Friedensfest lokal", scope: "CITY", calendarName: "Augsburg lokal", sourceType: "CUSTOM", }), ]), ); }); it("formats resolved holidays by region for assistant consumption", async () => { const db = { holidayCalendar: { findMany: vi.fn().mockResolvedValue([]), }, }; const caller = createProtectedCaller(db); const result = await caller.resolveHolidaysDetail({ countryCode: "DE", stateCode: "BY", periodStart: new Date("2026-01-01T00:00:00.000Z"), periodEnd: new Date("2026-12-31T00:00:00.000Z"), }); expect(result.locationContext).toEqual({ countryId: null, countryCode: "DE", federalState: "BY", metroCityId: null, metroCity: null, }); expect(result.count).toBeGreaterThan(0); expect(result.summary.byScope).toEqual( expect.arrayContaining([expect.objectContaining({ scope: "STATE" })]), ); expect(result.holidays).toEqual( expect.arrayContaining([ expect.objectContaining({ name: "Heilige Drei Könige", date: "2026-01-06", scope: "STATE" }), ]), ); }); it("formats resolved holidays for a resource including local city holidays", async () => { const db = { resource: { findFirst: vi.fn().mockResolvedValue({ id: "res_1" }), findUnique: vi.fn().mockResolvedValue({ 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" }, }), }, 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: "Augsburger Friedensfest", isRecurringAnnual: true, source: "manual", }, ], }, ]), }, }; const caller = createProtectedCaller(db); const result = await caller.resolveResourceHolidaysDetail({ resourceId: "res_1", periodStart: new Date("2026-01-01T00:00:00.000Z"), periodEnd: new Date("2026-12-31T00:00:00.000Z"), }); expect(result.resource).toEqual( expect.objectContaining({ eid: "bruce.banner", federalState: "BY", metroCity: "Augsburg", }), ); expect(result.summary.byScope).toEqual( expect.arrayContaining([expect.objectContaining({ scope: "CITY" })]), ); expect(result.holidays).toEqual( expect.arrayContaining([ expect.objectContaining({ name: "Augsburger Friedensfest", date: "2026-08-08", scope: "CITY", }), ]), ); }); it("rejects duplicate calendar scopes on create", async () => { const db = { country: { findUnique: vi .fn() .mockResolvedValueOnce({ id: "country_de", name: "Deutschland" }) .mockResolvedValueOnce({ id: "country_de", name: "Deutschland" }), }, metroCity: { findUnique: vi.fn(), }, holidayCalendar: { findFirst: vi.fn().mockResolvedValue({ id: "existing_scope" }), create: vi.fn(), }, auditLog: { create: vi.fn().mockResolvedValue({}), }, }; const caller = createAdminCaller(db); await expect(caller.createCalendar({ name: "Deutschland Standard", scopeType: "COUNTRY", countryId: "country_de", priority: 0, isActive: true, })).rejects.toThrow("A holiday calendar for this exact scope already exists"); expect(db.holidayCalendar.create).not.toHaveBeenCalled(); }); it("rejects duplicate entry dates within the same calendar", async () => { const db = { holidayCalendar: { findUnique: vi.fn().mockResolvedValue({ id: "cal_1", name: "Deutschland Standard" }), }, holidayCalendarEntry: { findFirst: vi.fn().mockResolvedValue({ id: "entry_existing" }), create: vi.fn(), }, auditLog: { create: vi.fn().mockResolvedValue({}), }, }; const caller = createAdminCaller(db); await expect(caller.createEntry({ holidayCalendarId: "cal_1", date: new Date("2026-12-24T00:00:00.000Z"), name: "Heiligabend lokal", isRecurringAnnual: true, source: "manual", })).rejects.toThrow("A holiday entry for this calendar and date already exists"); expect(db.holidayCalendarEntry.create).not.toHaveBeenCalled(); }); });