* send bookingId in BOOKING_CANCELLED through requestReschedule * fix: make customInputs nullable in BookingWebhookFactory * fix: make customInputs nullable in BookingWebhookFactory
319 lines
10 KiB
TypeScript
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("");
|
|
});
|
|
});
|
|
});
|