Files
calendar/packages/lib/server/service/__tests__/BookingWebhookFactory.test.ts
T
Hariom BalharaandGitHub aa618dbaa2 fix: Missing bookingId in BOOKING_CANCELLED webhook payload (#22713)
* send bookingId in BOOKING_CANCELLED through requestReschedule

* fix: make customInputs nullable in BookingWebhookFactory

* fix: make customInputs nullable in BookingWebhookFactory
2025-08-01 06:04:53 +00:00

319 lines
10 KiB
TypeScript

import { describe, it, expect } from "vitest";
import type { Person } from "@calcom/types/Calendar";
import { BookingWebhookFactory } from "../BookingWebhookFactory";
const createTestOrganizer = (overrides?: Partial<Person>): Person => ({
email: "organizer@example.com",
name: "Test Organizer",
timeZone: "UTC",
language: { locale: "en", translate: (() => "") as any },
...overrides,
});
const createTestAttendee = (overrides?: Partial<Person>): Person => ({
email: "attendee@example.com",
name: "Test Attendee",
timeZone: "UTC",
language: { locale: "en", translate: (() => "") as any },
...overrides,
});
const createTestBooking = (overrides?: Record<string, any>) => ({
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("");
});
});
});