import { SystemRole } from "@capakraken/shared"; import { describe, expect, it, vi } from "vitest"; import { createCallerFactory, createTRPCRouter } from "../trpc.js"; import { holidayCalendarCatalogReadProcedures } from "../router/holiday-calendar-catalog-read.js"; // Pass-through so the db mock reaches the procedures unchanged vi.mock("../router/holiday-calendar-shared.js", () => ({ asHolidayCalendarDb: (db: unknown) => db, })); // Provide the same shape the real support module exports vi.mock("../router/holiday-calendar-support.js", () => ({ holidayCalendarListInclude: { country: { select: { id: true, code: true, name: true } }, metroCity: { select: { id: true, name: true } }, entries: { orderBy: [{ date: "asc" }, { name: "asc" }] }, _count: { select: { entries: true } }, }, holidayCalendarDetailInclude: { country: { select: { id: true, code: true, name: true } }, metroCity: { select: { id: true, name: true } }, entries: { orderBy: [{ date: "asc" }, { name: "asc" }] }, }, })); const router = createTRPCRouter(holidayCalendarCatalogReadProcedures); const createCaller = createCallerFactory(router); function createAdminCaller(db: Record) { return createCaller({ session: { user: { email: "admin@example.com", name: "Admin", image: null }, expires: "2099-01-01T00:00:00.000Z", }, db: db as never, dbUser: { id: "user_admin", systemRole: SystemRole.ADMIN, permissionOverrides: null }, }); } function createUserCaller(db: Record) { return createCaller({ session: { user: { email: "user@example.com", name: "User", image: null }, expires: "2099-01-01T00:00:00.000Z", }, db: db as never, dbUser: { id: "user_1", systemRole: SystemRole.USER, permissionOverrides: null }, }); } const mockCalendar = { id: "cal_1", name: "German Federal Holidays", scopeType: "COUNTRY", stateCode: null, isActive: true, priority: 0, country: { id: "country_de", code: "DE", name: "Germany" }, metroCity: null, _count: { entries: 12 }, entries: [ { id: "entry_1", date: new Date("2026-01-01"), name: "New Year", isRecurringAnnual: true, source: null, }, ], }; // ─── listCalendars ──────────────────────────────────────────────────────────── describe("listCalendars", () => { it("returns all active calendars when called with no input", async () => { const findMany = vi.fn().mockResolvedValue([mockCalendar]); const caller = createAdminCaller({ holidayCalendar: { findMany } }); const result = await caller.listCalendars(); expect(result).toEqual([mockCalendar]); expect(findMany).toHaveBeenCalledWith( expect.objectContaining({ where: expect.objectContaining({ isActive: true }), }), ); }); it("applies countryCode filter uppercased with case-insensitive mode", async () => { const findMany = vi.fn().mockResolvedValue([mockCalendar]); const caller = createAdminCaller({ holidayCalendar: { findMany } }); await caller.listCalendars({ countryCode: "de" }); expect(findMany).toHaveBeenCalledWith( expect.objectContaining({ where: expect.objectContaining({ country: { code: { equals: "DE", mode: "insensitive" } }, }), }), ); }); it("includes inactive calendars when includeInactive is true", async () => { const inactiveCalendar = { ...mockCalendar, id: "cal_inactive", isActive: false }; const findMany = vi.fn().mockResolvedValue([mockCalendar, inactiveCalendar]); const caller = createAdminCaller({ holidayCalendar: { findMany } }); const result = await caller.listCalendars({ includeInactive: true }); expect(result).toHaveLength(2); // isActive filter must NOT be present in the where clause const whereArg = findMany.mock.calls[0][0].where; expect(whereArg).not.toHaveProperty("isActive"); }); }); // ─── listCalendarsDetail ────────────────────────────────────────────────────── describe("listCalendarsDetail", () => { it("returns count and formatted calendar array", async () => { const findMany = vi.fn().mockResolvedValue([mockCalendar]); const caller = createAdminCaller({ holidayCalendar: { findMany } }); const result = await caller.listCalendarsDetail(); expect(result.count).toBe(1); expect(result.calendars).toHaveLength(1); expect(result.calendars[0]).toMatchObject({ id: "cal_1", name: "German Federal Holidays", scopeType: "COUNTRY", stateCode: null, isActive: true, priority: 0, country: { id: "country_de", code: "DE", name: "Germany" }, metroCity: null, entryCount: 12, }); }); it("formats entry dates as ISO date strings (YYYY-MM-DD)", async () => { const findMany = vi.fn().mockResolvedValue([mockCalendar]); const caller = createAdminCaller({ holidayCalendar: { findMany } }); const result = await caller.listCalendarsDetail(); expect(result.calendars[0].entries[0]).toMatchObject({ id: "entry_1", date: "2026-01-01", name: "New Year", isRecurringAnnual: true, source: null, }); }); }); // ─── getCalendarByIdentifier ────────────────────────────────────────────────── describe("getCalendarByIdentifier", () => { it("finds a calendar by exact id on the first lookup", async () => { const findUnique = vi.fn().mockResolvedValue(mockCalendar); const findFirst = vi.fn(); const caller = createAdminCaller({ holidayCalendar: { findUnique, findFirst } }); const result = await caller.getCalendarByIdentifier({ identifier: "cal_1" }); expect(result).toEqual(mockCalendar); expect(findUnique).toHaveBeenCalledWith(expect.objectContaining({ where: { id: "cal_1" } })); expect(findFirst).not.toHaveBeenCalled(); }); it("falls back to exact name match when id lookup returns null", async () => { const findUnique = vi.fn().mockResolvedValue(null); const findFirst = vi.fn().mockResolvedValueOnce(mockCalendar); const caller = createAdminCaller({ holidayCalendar: { findUnique, findFirst } }); const result = await caller.getCalendarByIdentifier({ identifier: "German Federal Holidays" }); expect(result).toEqual(mockCalendar); expect(findFirst).toHaveBeenCalledWith( expect.objectContaining({ where: { name: { equals: "German Federal Holidays", mode: "insensitive" } }, }), ); }); it("falls back to contains name match when exact name lookup returns null", async () => { const findUnique = vi.fn().mockResolvedValue(null); const findFirst = vi .fn() .mockResolvedValueOnce(null) // exact name miss .mockResolvedValueOnce(mockCalendar); // contains name hit const caller = createAdminCaller({ holidayCalendar: { findUnique, findFirst } }); const result = await caller.getCalendarByIdentifier({ identifier: "Federal" }); expect(result).toEqual(mockCalendar); expect(findFirst).toHaveBeenCalledTimes(2); expect(findFirst).toHaveBeenLastCalledWith( expect.objectContaining({ where: { name: { contains: "Federal", mode: "insensitive" } }, }), ); }); it("throws NOT_FOUND when no match is found at all", async () => { const findUnique = vi.fn().mockResolvedValue(null); const findFirst = vi.fn().mockResolvedValue(null); const caller = createAdminCaller({ holidayCalendar: { findUnique, findFirst } }); await expect( caller.getCalendarByIdentifier({ identifier: "does-not-exist" }), ).rejects.toMatchObject({ code: "NOT_FOUND", message: "Holiday calendar not found: does-not-exist", }); }); }); // ─── getCalendarByIdentifierDetail ──────────────────────────────────────────── describe("getCalendarByIdentifierDetail", () => { it("returns formatted detail for a matching calendar", async () => { const findUnique = vi.fn().mockResolvedValue(mockCalendar); const caller = createAdminCaller({ holidayCalendar: { findUnique, findFirst: vi.fn() } }); const result = await caller.getCalendarByIdentifierDetail({ identifier: "cal_1" }); expect(result).toMatchObject({ id: "cal_1", name: "German Federal Holidays", scopeType: "COUNTRY", isActive: true, entryCount: 12, entries: [expect.objectContaining({ date: "2026-01-01" })], }); }); it("throws NOT_FOUND when identifier does not match any calendar", async () => { const findUnique = vi.fn().mockResolvedValue(null); const findFirst = vi.fn().mockResolvedValue(null); const caller = createAdminCaller({ holidayCalendar: { findUnique, findFirst } }); await expect( caller.getCalendarByIdentifierDetail({ identifier: "no-such-calendar" }), ).rejects.toMatchObject({ code: "NOT_FOUND" }); }); }); // ─── getCalendarById ────────────────────────────────────────────────────────── describe("getCalendarById", () => { it("returns the calendar when found by id", async () => { const findUnique = vi.fn().mockResolvedValue(mockCalendar); const caller = createAdminCaller({ holidayCalendar: { findUnique } }); const result = await caller.getCalendarById({ id: "cal_1" }); expect(result).toEqual(mockCalendar); expect(findUnique).toHaveBeenCalledWith(expect.objectContaining({ where: { id: "cal_1" } })); }); it("throws NOT_FOUND when no calendar exists with the given id", async () => { const findUnique = vi.fn().mockResolvedValue(null); const caller = createAdminCaller({ holidayCalendar: { findUnique } }); await expect(caller.getCalendarById({ id: "cal_missing" })).rejects.toMatchObject({ code: "NOT_FOUND", message: "Holiday calendar not found", }); }); }); // ─── Authorization ──────────────────────────────────────────────────────────── describe("Authorization", () => { it("rejects a non-admin user (SystemRole.USER) with FORBIDDEN on all procedures", async () => { const findMany = vi.fn(); const findUnique = vi.fn(); const findFirst = vi.fn(); const caller = createUserCaller({ holidayCalendar: { findMany, findUnique, findFirst } }); await expect(caller.listCalendars()).rejects.toMatchObject({ code: "FORBIDDEN", message: "Admin role required", }); await expect(caller.listCalendarsDetail()).rejects.toMatchObject({ code: "FORBIDDEN", message: "Admin role required", }); await expect(caller.getCalendarByIdentifier({ identifier: "cal_1" })).rejects.toMatchObject({ code: "FORBIDDEN", message: "Admin role required", }); await expect( caller.getCalendarByIdentifierDetail({ identifier: "cal_1" }), ).rejects.toMatchObject({ code: "FORBIDDEN", message: "Admin role required", }); await expect(caller.getCalendarById({ id: "cal_1" })).rejects.toMatchObject({ code: "FORBIDDEN", message: "Admin role required", }); expect(findMany).not.toHaveBeenCalled(); expect(findUnique).not.toHaveBeenCalled(); expect(findFirst).not.toHaveBeenCalled(); }); });