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("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("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(); }); });