import { describe, test, expect, vi, beforeEach } from "vitest"; import type { CalendarSubscriptionEventItem } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionPort.interface"; import type { ICalendarCacheEventRepository } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventRepository.interface"; import type { SelectedCalendar } from "@calcom/prisma/client"; import { CalendarCacheEventService } from "../CalendarCacheEventService"; const mockSelectedCalendar: SelectedCalendar = { id: "test-calendar-id", userId: 1, credentialId: 1, integration: "google_calendar", externalId: "test@example.com", eventTypeId: null, delegationCredentialId: null, domainWideDelegationCredentialId: null, googleChannelId: null, googleChannelKind: null, googleChannelResourceId: null, googleChannelResourceUri: null, googleChannelExpiration: null, error: null, lastErrorAt: null, watchAttempts: 0, maxAttempts: 3, unwatchAttempts: 0, createdAt: new Date(), updatedAt: new Date(), channelId: "test-channel-id", channelKind: "web_hook", channelResourceId: "test-resource-id", channelResourceUri: "test-resource-uri", channelExpiration: new Date(Date.now() + 86400000), syncSubscribedAt: new Date(), syncSubscribedErrorAt: null, syncSubscribedErrorCount: 0, syncToken: "test-sync-token", syncedAt: new Date(), syncErrorAt: null, syncErrorCount: 0, }; describe("CalendarCacheEventService", () => { let service: CalendarCacheEventService; let mockRepository: ICalendarCacheEventRepository; beforeEach(() => { mockRepository = { upsertMany: vi.fn().mockResolvedValue(undefined), deleteMany: vi.fn().mockResolvedValue(undefined), deleteAllBySelectedCalendarId: vi.fn().mockResolvedValue(undefined), deleteStale: vi.fn().mockResolvedValue(undefined), findAllBySelectedCalendarIdsBetween: vi.fn().mockResolvedValue([]), }; service = new CalendarCacheEventService({ calendarCacheEventRepository: mockRepository, }); }); describe("handleEvents", () => { test("should process busy events and store them in cache", async () => { const events: CalendarSubscriptionEventItem[] = [ { id: "event-1", iCalUID: "event-1@cal.com", start: new Date("2023-12-01T10:00:00Z"), end: new Date("2023-12-01T11:00:00Z"), busy: true, summary: "Test Event", description: "Test Description", location: "Test Location", status: "confirmed", isAllDay: false, timeZone: "UTC", recurringEventId: null, originalStartDate: null, createdAt: new Date("2023-12-01T09:00:00Z"), updatedAt: new Date("2023-12-01T09:30:00Z"), etag: "test-etag", kind: "calendar#event", }, ]; await service.handleEvents(mockSelectedCalendar, events); expect(mockRepository.upsertMany).toHaveBeenCalledWith([ { externalId: "event-1", selectedCalendarId: "test-calendar-id", start: new Date("2023-12-01T10:00:00Z"), end: new Date("2023-12-01T11:00:00Z"), summary: "Test Event", description: "Test Description", location: "Test Location", isAllDay: false, timeZone: "UTC", originalStartTime: null, recurringEventId: null, externalEtag: "test-etag", externalCreatedAt: new Date("2023-12-01T09:00:00Z"), externalUpdatedAt: new Date("2023-12-01T09:30:00Z"), }, ]); expect(mockRepository.deleteMany).toHaveBeenCalledWith([]); }); test("should not store free events in cache", async () => { const events: CalendarSubscriptionEventItem[] = [ { id: "event-1", iCalUID: "event-1@cal.com", start: new Date("2023-12-01T10:00:00Z"), end: new Date("2023-12-01T11:00:00Z"), busy: false, summary: "Free Event", description: "Free Description", location: "Free Location", status: "confirmed", isAllDay: false, timeZone: "UTC", recurringEventId: null, originalStartDate: null, createdAt: new Date("2023-12-01T09:00:00Z"), updatedAt: new Date("2023-12-01T09:30:00Z"), etag: "test-etag", kind: "calendar#event", }, ]; await service.handleEvents(mockSelectedCalendar, events); expect(mockRepository.upsertMany).toHaveBeenCalledWith([]); expect(mockRepository.deleteMany).toHaveBeenCalledWith([ { selectedCalendarId: "test-calendar-id", externalId: "event-1", }, ]); }); test("should delete cancelled events from cache", async () => { const events: CalendarSubscriptionEventItem[] = [ { id: "event-1", iCalUID: "event-1@cal.com", start: new Date("2023-12-01T10:00:00Z"), end: new Date("2023-12-01T11:00:00Z"), busy: true, summary: "Cancelled Event", description: "Cancelled Description", location: "Cancelled Location", status: "cancelled", isAllDay: false, timeZone: "UTC", recurringEventId: null, originalStartDate: null, createdAt: new Date("2023-12-01T09:00:00Z"), updatedAt: new Date("2023-12-01T09:30:00Z"), etag: "test-etag", kind: "calendar#event", }, ]; await service.handleEvents(mockSelectedCalendar, events); expect(mockRepository.upsertMany).toHaveBeenCalledWith([]); expect(mockRepository.deleteMany).toHaveBeenCalledWith([ { selectedCalendarId: "test-calendar-id", externalId: "event-1", }, ]); }); test("should handle mixed event types correctly", async () => { const events: CalendarSubscriptionEventItem[] = [ { id: "event-1", iCalUID: "event-1@cal.com", start: new Date("2023-12-01T10:00:00Z"), end: new Date("2023-12-01T11:00:00Z"), busy: true, summary: "Busy Event", description: "Busy Description", location: "Busy Location", status: "confirmed", isAllDay: false, timeZone: "UTC", recurringEventId: null, originalStartDate: null, createdAt: new Date("2023-12-01T09:00:00Z"), updatedAt: new Date("2023-12-01T09:30:00Z"), etag: "test-etag-1", kind: "calendar#event", }, { id: "event-2", iCalUID: "event-2@cal.com", start: new Date("2023-12-01T12:00:00Z"), end: new Date("2023-12-01T13:00:00Z"), busy: false, summary: "Free Event", description: "Free Description", location: "Free Location", status: "confirmed", isAllDay: false, timeZone: "UTC", recurringEventId: null, originalStartDate: null, createdAt: new Date("2023-12-01T11:00:00Z"), updatedAt: new Date("2023-12-01T11:30:00Z"), etag: "test-etag-2", kind: "calendar#event", }, { id: "event-3", iCalUID: "event-3@cal.com", start: new Date("2023-12-01T14:00:00Z"), end: new Date("2023-12-01T15:00:00Z"), busy: true, summary: "Cancelled Event", description: "Cancelled Description", location: "Cancelled Location", status: "cancelled", isAllDay: false, timeZone: "UTC", recurringEventId: null, originalStartDate: null, createdAt: new Date("2023-12-01T13:00:00Z"), updatedAt: new Date("2023-12-01T13:30:00Z"), etag: "test-etag-3", kind: "calendar#event", }, ]; await service.handleEvents(mockSelectedCalendar, events); expect(mockRepository.upsertMany).toHaveBeenCalledWith([ { externalId: "event-1", selectedCalendarId: "test-calendar-id", start: new Date("2023-12-01T10:00:00Z"), end: new Date("2023-12-01T11:00:00Z"), summary: "Busy Event", description: "Busy Description", location: "Busy Location", isAllDay: false, timeZone: "UTC", originalStartTime: null, recurringEventId: null, externalEtag: "test-etag-1", externalCreatedAt: new Date("2023-12-01T09:00:00Z"), externalUpdatedAt: new Date("2023-12-01T09:30:00Z"), }, ]); expect(mockRepository.deleteMany).toHaveBeenCalledWith([ { selectedCalendarId: "test-calendar-id", externalId: "event-2", }, { selectedCalendarId: "test-calendar-id", externalId: "event-3", }, ]); }); test("should handle empty events array", async () => { await service.handleEvents(mockSelectedCalendar, []); expect(mockRepository.upsertMany).toHaveBeenCalledWith([]); expect(mockRepository.deleteMany).toHaveBeenCalledWith([]); }); test("should handle events with missing optional fields", async () => { const events: CalendarSubscriptionEventItem[] = [ { id: "event-1", iCalUID: "event-1@cal.com", start: new Date("2023-12-01T10:00:00Z"), end: new Date("2023-12-01T11:00:00Z"), busy: true, summary: null, description: null, location: null, status: "confirmed", isAllDay: false, timeZone: null, recurringEventId: null, originalStartDate: null, createdAt: null, updatedAt: null, etag: null, kind: null, }, ]; await service.handleEvents(mockSelectedCalendar, events); expect(mockRepository.upsertMany).toHaveBeenCalledWith([ { externalId: "event-1", selectedCalendarId: "test-calendar-id", start: new Date("2023-12-01T10:00:00Z"), end: new Date("2023-12-01T11:00:00Z"), summary: null, description: null, location: null, isAllDay: false, timeZone: null, originalStartTime: null, recurringEventId: null, externalEtag: "", externalCreatedAt: null, externalUpdatedAt: null, }, ]); }); }); describe("cleanupCache", () => { test("should delete all events for selected calendar", async () => { await service.cleanupCache(mockSelectedCalendar); expect(mockRepository.deleteAllBySelectedCalendarId).toHaveBeenCalledWith("test-calendar-id"); }); }); describe("cleanupStaleCache", () => { test("should delete stale events", async () => { await service.cleanupStaleCache(); expect(mockRepository.deleteStale).toHaveBeenCalled(); }); }); describe("isCalendarTypeSupported", () => { test("should return true for supported calendar types", () => { expect(CalendarCacheEventService.isCalendarTypeSupported("google_calendar")).toBe(true); }); test("should return false for unsupported calendar types", () => { expect(CalendarCacheEventService.isCalendarTypeSupported("office365_calendar")).toBe(false); expect(CalendarCacheEventService.isCalendarTypeSupported("outlook_calendar")).toBe(false); expect(CalendarCacheEventService.isCalendarTypeSupported("apple_calendar")).toBe(false); expect(CalendarCacheEventService.isCalendarTypeSupported("unknown_calendar")).toBe(false); }); test("should return false for null", () => { expect(CalendarCacheEventService.isCalendarTypeSupported(null)).toBe(false); }); }); });