import { describe, it, expect, vi } from "vitest"; import { BookingStatus, WebhookTriggerEvents } from "@calcom/prisma/enums"; import type { CalendarEvent } from "@calcom/types/Calendar"; import type { BookingWebhookEventDTO, EventTypeInfo } from "../../dto/types"; import { BookingPayloadBuilder } from "../versioned/v2021-10-20/BookingPayloadBuilder"; vi.mock("@calcom/lib/dayjs", () => ({ getUTCOffsetByTimezone: vi.fn(() => 0), })); describe("BookingPayloadBuilder (v2021-10-20)", () => { const mockEventType: EventTypeInfo = { eventTitle: "Test Event", eventDescription: "Test Description", requiresConfirmation: false, price: 0, currency: "USD", length: 30, }; const mockCalendarEvent: CalendarEvent = { type: "test-event", title: "Test Meeting", description: "Meeting description", additionalNotes: "Additional notes", startTime: "2024-01-15T10:00:00Z", endTime: "2024-01-15T10:30:00Z", organizer: { id: 1, email: "organizer@test.com", name: "Test Organizer", timeZone: "UTC", language: { locale: "en" }, }, attendees: [ { email: "attendee@test.com", name: "Test Attendee", timeZone: "UTC", language: { locale: "en" }, }, ], location: "https://cal.com/video/123", uid: "booking-uid-123", customInputs: {}, responses: {}, userFieldsResponses: {}, }; const createMockDTO = ( triggerEvent: WebhookTriggerEvents, extra: Partial = {} ): BookingWebhookEventDTO => ({ triggerEvent, createdAt: "2024-01-15T10:00:00Z", booking: { id: 1, eventTypeId: 1, userId: 1, smsReminderNumber: null, }, eventType: mockEventType, evt: mockCalendarEvent, ...extra, }); const builder = new BookingPayloadBuilder(); describe("BOOKING_CREATED", () => { it("should build payload with ACCEPTED status", () => { const dto = createMockDTO(WebhookTriggerEvents.BOOKING_CREATED); const payload = builder.build(dto); expect(payload.triggerEvent).toBe(WebhookTriggerEvents.BOOKING_CREATED); expect(payload.payload.status).toBe(BookingStatus.ACCEPTED); expect(payload.payload.bookingId).toBe(1); expect(payload.payload.title).toBe("Test Meeting"); expect(payload.payload.organizer.email).toBe("organizer@test.com"); expect(payload.payload.attendees).toHaveLength(1); }); it("should include eventType fields", () => { const dto = createMockDTO(WebhookTriggerEvents.BOOKING_CREATED); const payload = builder.build(dto); expect(payload.payload.eventTitle).toBe("Test Event"); expect(payload.payload.eventDescription).toBe("Test Description"); expect(payload.payload.price).toBe(0); expect(payload.payload.currency).toBe("USD"); expect(payload.payload.length).toBe(30); }); }); describe("BOOKING_CANCELLED", () => { it("should build payload with CANCELLED status and extra fields", () => { const dto = createMockDTO(WebhookTriggerEvents.BOOKING_CANCELLED, { cancelledBy: "user@test.com", cancellationReason: "Schedule conflict", }); const payload = builder.build(dto); expect(payload.triggerEvent).toBe(WebhookTriggerEvents.BOOKING_CANCELLED); expect(payload.payload.status).toBe(BookingStatus.CANCELLED); expect(payload.payload.cancelledBy).toBe("user@test.com"); expect(payload.payload.cancellationReason).toBe("Schedule conflict"); }); }); describe("BOOKING_REQUESTED", () => { it("should build payload with PENDING status", () => { const dto = createMockDTO(WebhookTriggerEvents.BOOKING_REQUESTED); const payload = builder.build(dto); expect(payload.triggerEvent).toBe(WebhookTriggerEvents.BOOKING_REQUESTED); expect(payload.payload.status).toBe(BookingStatus.PENDING); }); }); describe("BOOKING_REJECTED", () => { it("should build payload with REJECTED status", () => { const dto = createMockDTO(WebhookTriggerEvents.BOOKING_REJECTED); const payload = builder.build(dto); expect(payload.triggerEvent).toBe(WebhookTriggerEvents.BOOKING_REJECTED); expect(payload.payload.status).toBe(BookingStatus.REJECTED); }); }); describe("BOOKING_RESCHEDULED", () => { it("should build payload with reschedule extra fields", () => { const dto = createMockDTO(WebhookTriggerEvents.BOOKING_RESCHEDULED, { rescheduleId: 2, rescheduleUid: "reschedule-uid-456", rescheduleStartTime: "2024-01-16T10:00:00Z", rescheduleEndTime: "2024-01-16T10:30:00Z", rescheduledBy: "user@test.com", }); const payload = builder.build(dto); expect(payload.triggerEvent).toBe(WebhookTriggerEvents.BOOKING_RESCHEDULED); expect(payload.payload.status).toBe(BookingStatus.ACCEPTED); expect(payload.payload.rescheduleId).toBe(2); expect(payload.payload.rescheduleUid).toBe("reschedule-uid-456"); expect(payload.payload.rescheduledBy).toBe("user@test.com"); }); }); describe("BOOKING_PAID", () => { it("should build payload with payment extra fields", () => { const dto = createMockDTO(WebhookTriggerEvents.BOOKING_PAID, { paymentId: 123, paymentData: { stripeId: "stripe_123" }, }); const payload = builder.build(dto); expect(payload.triggerEvent).toBe(WebhookTriggerEvents.BOOKING_PAID); expect(payload.payload.paymentId).toBe(123); expect(payload.payload.paymentData).toEqual({ stripeId: "stripe_123" }); }); }); describe("BOOKING_PAYMENT_INITIATED", () => { it("should build payload with payment extra fields", () => { const dto = createMockDTO(WebhookTriggerEvents.BOOKING_PAYMENT_INITIATED, { paymentId: 456, paymentData: { status: "pending" }, }); const payload = builder.build(dto); expect(payload.triggerEvent).toBe(WebhookTriggerEvents.BOOKING_PAYMENT_INITIATED); expect(payload.payload.paymentId).toBe(456); }); }); describe("BOOKING_NO_SHOW_UPDATED", () => { it("should build no-show specific payload", () => { const dto: BookingWebhookEventDTO = { triggerEvent: WebhookTriggerEvents.BOOKING_NO_SHOW_UPDATED, createdAt: "2024-01-15T10:00:00Z", bookingUid: "booking-uid-123", bookingId: 1, attendees: [{ email: "attendee@test.com", noShow: true }], message: "Attendee marked as no-show", } as BookingWebhookEventDTO; const payload = builder.build(dto); expect(payload.triggerEvent).toBe(WebhookTriggerEvents.BOOKING_NO_SHOW_UPDATED); expect(payload.payload.bookingUid).toBe("booking-uid-123"); expect(payload.payload.bookingId).toBe(1); expect(payload.payload.message).toBe("Attendee marked as no-show"); }); }); describe("attendee UTC offset", () => { it("should add utcOffset to each attendee", () => { const dto = createMockDTO(WebhookTriggerEvents.BOOKING_CREATED); const payload = builder.build(dto); expect(payload.payload.attendees[0]).toHaveProperty("utcOffset"); }); }); describe("organizer UTC offset", () => { it("should add utcOffset to organizer", () => { const dto = createMockDTO(WebhookTriggerEvents.BOOKING_CREATED); const payload = builder.build(dto); expect(payload.payload.organizer).toHaveProperty("utcOffset"); }); }); });