import { describe, it, expect } from "vitest"; import type { Person } from "@calcom/types/Calendar"; import { BookingWebhookFactory } from "../BookingWebhookFactory"; const createTestOrganizer = (overrides?: Partial): Person => ({ email: "organizer@example.com", name: "Test Organizer", timeZone: "UTC", language: { locale: "en", translate: (() => "") as any }, ...overrides, }); const createTestAttendee = (overrides?: Partial): Person => ({ email: "attendee@example.com", name: "Test Attendee", timeZone: "UTC", language: { locale: "en", translate: (() => "") as any }, ...overrides, }); const createTestBooking = (overrides?: Record) => ({ id: 12345, uid: "test-uid-123", title: "Test Event", description: "Test Description", startTime: new Date("2025-01-24T10:00:00Z"), endTime: new Date("2025-01-24T11:00:00Z"), location: "https://meet.example.com", customInputs: { field1: "value1" }, responses: { name: { value: "Test User" } }, userFieldsResponses: { company: { value: "Test Corp" } }, smsReminderNumber: "+1234567890", iCalUID: "ical-uid-123", ...overrides, }); describe("BookingWebhookFactory", () => { describe("createCancelledEventPayload", () => { it("should create a basic cancelled event payload with required fields", () => { const factory = new BookingWebhookFactory(); const booking = createTestBooking(); const organizer = createTestOrganizer(); const attendees = [createTestAttendee()]; const payload = factory.createCancelledEventPayload({ bookingId: booking.id, uid: booking.uid, title: booking.title, eventSlug: "test-event", description: booking.description, startTime: booking.startTime.toISOString(), endTime: booking.endTime.toISOString(), location: booking.location, customInputs: booking.customInputs, responses: booking.responses, userFieldsResponses: booking.userFieldsResponses, smsReminderNumber: booking.smsReminderNumber, iCalUID: booking.iCalUID, organizer, attendees, destinationCalendar: null, cancelledBy: "test@example.com", cancellationReason: "User requested", }); expect(payload.bookingId).toBe(12345); expect(payload.uid).toBe("test-uid-123"); expect(payload.title).toBe("Test Event"); expect(payload.description).toBe("Test Description"); expect(payload.organizer).toEqual(organizer); expect(payload.attendees).toEqual(attendees); expect(payload.type).toBe("test-event"); expect(payload.startTime).toBe(booking.startTime.toISOString()); expect(payload.endTime).toBe(booking.endTime.toISOString()); expect(payload.location).toBe("https://meet.example.com"); expect(payload.customInputs).toEqual({ field1: "value1" }); expect(payload.responses).toEqual({ name: { value: "Test User" } }); expect(payload.userFieldsResponses).toEqual({ company: { value: "Test Corp" } }); expect(payload.smsReminderNumber).toBe("+1234567890"); expect(payload.iCalUID).toBe("ical-uid-123"); expect(payload.destinationCalendar).toEqual([]); expect(payload.cancellationReason).toBe("User requested"); expect(payload.cancelledBy).toBe("test@example.com"); // Verify no extra properties exist const expectedKeys = [ "bookingId", "type", "title", "description", "customInputs", "responses", "userFieldsResponses", "startTime", "endTime", "organizer", "attendees", "uid", "location", "destinationCalendar", "iCalUID", "smsReminderNumber", "cancellationReason", "cancelledBy", ]; const actualKeys = Object.keys(payload).sort(); expect(actualKeys).toEqual(expectedKeys.sort()); }); it("should handle responses and userFieldsResponses", () => { const factory = new BookingWebhookFactory(); const booking = createTestBooking(); const responses = { field1: "response1", field2: "response2" }; const userFieldsResponses = { company: "Test Corp", department: "Engineering" }; const payload = factory.createCancelledEventPayload({ bookingId: booking.id, uid: booking.uid, title: booking.title, eventSlug: null, description: null, startTime: booking.startTime.toISOString(), endTime: booking.endTime.toISOString(), location: null, customInputs: null, responses, userFieldsResponses, smsReminderNumber: null, iCalUID: null, organizer: createTestOrganizer(), attendees: [createTestAttendee()], destinationCalendar: null, cancelledBy: "user@example.com", cancellationReason: "Test", }); expect(payload.responses).toEqual(responses); expect(payload.userFieldsResponses).toEqual(userFieldsResponses); }); it("should handle destination calendar as single object", () => { const factory = new BookingWebhookFactory(); const booking = createTestBooking(); const destinationCalendar = { id: 1, integration: "google", externalId: "cal123", primaryEmail: null, userId: null, eventTypeId: null, credentialId: null, createdAt: null, updatedAt: null, delegationCredentialId: null, domainWideDelegationCredentialId: null, }; const payload = factory.createCancelledEventPayload({ bookingId: booking.id, uid: booking.uid, title: booking.title, eventSlug: null, description: null, startTime: booking.startTime.toISOString(), endTime: booking.endTime.toISOString(), location: null, customInputs: null, responses: {}, userFieldsResponses: {}, smsReminderNumber: null, iCalUID: null, organizer: createTestOrganizer(), attendees: [], destinationCalendar, cancelledBy: "user@example.com", cancellationReason: "Test", }); expect(payload.destinationCalendar).toEqual([destinationCalendar]); }); it("should handle null destination calendar", () => { const factory = new BookingWebhookFactory(); const booking = createTestBooking(); const payload = factory.createCancelledEventPayload({ bookingId: booking.id, uid: booking.uid, title: booking.title, eventSlug: null, description: null, startTime: booking.startTime.toISOString(), endTime: booking.endTime.toISOString(), location: null, customInputs: null, responses: {}, userFieldsResponses: {}, smsReminderNumber: null, iCalUID: null, organizer: createTestOrganizer(), attendees: [], destinationCalendar: null, cancelledBy: "user@example.com", cancellationReason: "Test", }); expect(payload.destinationCalendar).toEqual([]); }); it("should handle sms reminder number and iCal UID", () => { const factory = new BookingWebhookFactory(); const booking = createTestBooking(); const payload = factory.createCancelledEventPayload({ bookingId: booking.id, uid: booking.uid, title: booking.title, eventSlug: null, description: null, startTime: booking.startTime.toISOString(), endTime: booking.endTime.toISOString(), location: null, customInputs: null, responses: {}, userFieldsResponses: {}, smsReminderNumber: "+1234567890", iCalUID: "ical-uid-123", organizer: createTestOrganizer(), attendees: [], destinationCalendar: null, cancelledBy: "user@example.com", cancellationReason: "Test", }); expect(payload.smsReminderNumber).toBe("+1234567890"); expect(payload.iCalUID).toBe("ical-uid-123"); }); it("should derive type from eventSlug when available, otherwise from title", () => { const factory = new BookingWebhookFactory(); const booking = createTestBooking(); // Test with eventSlug provided const payloadWithSlug = factory.createCancelledEventPayload({ bookingId: booking.id, uid: booking.uid, title: "Meeting Title", eventSlug: "team-standup", description: null, startTime: booking.startTime.toISOString(), endTime: booking.endTime.toISOString(), location: null, customInputs: null, responses: {}, userFieldsResponses: {}, smsReminderNumber: null, iCalUID: null, organizer: createTestOrganizer(), attendees: [], destinationCalendar: null, cancelledBy: "user@example.com", cancellationReason: "Test", }); expect(payloadWithSlug.type).toBe("team-standup"); // Test with eventSlug null, should use title const payloadWithoutSlug = factory.createCancelledEventPayload({ bookingId: booking.id, uid: booking.uid, title: "Meeting Title", eventSlug: null, description: null, startTime: booking.startTime.toISOString(), endTime: booking.endTime.toISOString(), location: null, customInputs: null, responses: {}, userFieldsResponses: {}, smsReminderNumber: null, iCalUID: null, organizer: createTestOrganizer(), attendees: [], destinationCalendar: null, cancelledBy: "user@example.com", cancellationReason: "Test", }); expect(payloadWithoutSlug.type).toBe("Meeting Title"); // Test with both eventSlug and title empty const payloadEmpty = factory.createCancelledEventPayload({ bookingId: booking.id, uid: booking.uid, title: "", eventSlug: "", description: null, startTime: booking.startTime.toISOString(), endTime: booking.endTime.toISOString(), location: null, customInputs: null, responses: {}, userFieldsResponses: {}, smsReminderNumber: null, iCalUID: null, organizer: createTestOrganizer(), attendees: [], destinationCalendar: null, cancelledBy: "user@example.com", cancellationReason: "Test", }); expect(payloadEmpty.type).toBe(""); }); }); });