import prismaMock from "../../../../../tests/libs/__mocks__/prisma"; import type { Request, Response } from "express"; import type { NextApiRequest, NextApiResponse } from "next"; import { createMocks } from "node-mocks-http"; import { describe, expect, test, vi } from "vitest"; import dayjs from "@calcom/dayjs"; import sendPayload from "@calcom/features/webhooks/lib/sendPayload"; import { buildBooking, buildEventType, buildWebhook } from "@calcom/lib/test/builder"; import prisma from "@calcom/prisma"; import handler from "../../../pages/api/bookings/_post"; type CustomNextApiRequest = NextApiRequest & Request; type CustomNextApiResponse = NextApiResponse & Response; vi.mock("@calcom/features/webhooks/lib/sendPayload"); vi.mock("@calcom/lib/server/i18n", () => { return { getTranslation: (key: string) => key, }; }); describe("POST /api/bookings", () => { describe("Errors", () => { test("Missing required data", async () => { const { req, res } = createMocks({ method: "POST", body: {}, }); await handler(req, res); expect(res._getStatusCode()).toBe(400); expect(JSON.parse(res._getData())).toEqual( expect.objectContaining({ message: "invalid_type in 'eventTypeId': Required; invalid_type in 'title': Required; invalid_type in 'startTime': Required; invalid_type in 'startTime': Required; invalid_type in 'endTime': Required; invalid_type in 'endTime': Required", }) ); }); test("Invalid eventTypeId", async () => { const { req, res } = createMocks({ method: "POST", body: { title: "test", eventTypeId: 2, startTime: dayjs().toDate(), endTime: dayjs().add(1, "day").toDate(), }, prisma, }); prismaMock.eventType.findUnique.mockResolvedValue(null); await handler(req, res); expect(res._getStatusCode()).toBe(400); expect(JSON.parse(res._getData())).toEqual( expect.objectContaining({ message: "'invalid_type' in 'email': Required; 'invalid_type' in 'end': Required; 'invalid_type' in 'location': Required; 'invalid_type' in 'name': Required; 'invalid_type' in 'start': Required; 'invalid_type' in 'timeZone': Required; 'invalid_type' in 'language': Required; 'invalid_type' in 'customInputs': Required; 'invalid_type' in 'metadata': Required", }) ); }); test("Missing recurringCount", async () => { const { req, res } = createMocks({ method: "POST", body: { title: "test", eventTypeId: 2, startTime: dayjs().toDate(), endTime: dayjs().add(1, "day").toDate(), }, prisma, }); prismaMock.eventType.findUnique.mockResolvedValue( buildEventType({ recurringEvent: { freq: 2, count: 12, interval: 1 } }) ); await handler(req, res); expect(res._getStatusCode()).toBe(400); expect(JSON.parse(res._getData())).toEqual( expect.objectContaining({ message: "'invalid_type' in 'email': Required; 'invalid_type' in 'end': Required; 'invalid_type' in 'location': Required; 'invalid_type' in 'name': Required; 'invalid_type' in 'start': Required; 'invalid_type' in 'timeZone': Required; 'invalid_type' in 'language': Required; 'invalid_type' in 'customInputs': Required; 'invalid_type' in 'metadata': Required", }) ); }); test("Invalid recurringCount", async () => { const { req, res } = createMocks({ method: "POST", body: { title: "test", eventTypeId: 2, startTime: dayjs().toDate(), endTime: dayjs().add(1, "day").toDate(), recurringCount: 15, }, prisma, }); prismaMock.eventType.findUnique.mockResolvedValue( buildEventType({ recurringEvent: { freq: 2, count: 12, interval: 1 } }) ); await handler(req, res); expect(res._getStatusCode()).toBe(400); expect(JSON.parse(res._getData())).toEqual( expect.objectContaining({ message: "'invalid_type' in 'email': Required; 'invalid_type' in 'end': Required; 'invalid_type' in 'location': Required; 'invalid_type' in 'name': Required; 'invalid_type' in 'start': Required; 'invalid_type' in 'timeZone': Required; 'invalid_type' in 'language': Required; 'invalid_type' in 'customInputs': Required; 'invalid_type' in 'metadata': Required", }) ); }); test("No available users", async () => { const { req, res } = createMocks({ method: "POST", body: { name: "test", start: dayjs().format(), end: dayjs().add(1, "day").format(), eventTypeId: 2, email: "test@example.com", location: "Cal.com Video", timeZone: "America/Montevideo", language: "en", customInputs: [], metadata: {}, userId: 4, }, prisma, }); prismaMock.eventType.findUniqueOrThrow.mockResolvedValue(buildEventType()); await handler(req, res); console.log({ statusCode: res._getStatusCode(), data: JSON.parse(res._getData()) }); expect(res._getStatusCode()).toBe(500); expect(JSON.parse(res._getData())).toEqual( expect.objectContaining({ message: "No available users found.", }) ); }); }); describe("Success", () => { describe("Regular event-type", () => { test("Creates one single booking", async () => { const { req, res } = createMocks({ method: "POST", body: { name: "test", start: dayjs().format(), end: dayjs().add(1, "day").format(), eventTypeId: 2, email: "test@example.com", location: "Cal.com Video", timeZone: "America/Montevideo", language: "en", customInputs: [], metadata: {}, userId: 4, }, prisma, }); prismaMock.eventType.findUniqueOrThrow.mockResolvedValue(buildEventType()); prismaMock.booking.findMany.mockResolvedValue([]); await handler(req, res); console.log({ statusCode: res._getStatusCode(), data: JSON.parse(res._getData()) }); expect(prismaMock.booking.create).toHaveBeenCalledTimes(1); }); }); describe("Recurring event-type", () => { test("Creates multiple bookings", async () => { const { req, res } = createMocks({ method: "POST", body: { title: "test", eventTypeId: 2, startTime: dayjs().toDate(), endTime: dayjs().add(1, "day").toDate(), recurringCount: 12, }, prisma, }); prismaMock.eventType.findUnique.mockResolvedValue( buildEventType({ recurringEvent: { freq: 2, count: 12, interval: 1 } }) ); Array.from(Array(12).keys()).map(async () => { prismaMock.booking.create.mockResolvedValue(buildBooking()); }); prismaMock.webhook.findMany.mockResolvedValue([]); await handler(req, res); const data = JSON.parse(res._getData()); expect(prismaMock.booking.create).toHaveBeenCalledTimes(12); expect(res._getStatusCode()).toBe(201); expect(data.message).toEqual("Bookings created successfully."); expect(data.bookings.length).toEqual(12); }); }); test("Notifies multiple bookings", async () => { const { req, res } = createMocks({ method: "POST", body: { title: "test", eventTypeId: 2, startTime: dayjs().toDate(), endTime: dayjs().add(1, "day").toDate(), recurringCount: 12, }, prisma, }); prismaMock.eventType.findUnique.mockResolvedValue( buildEventType({ recurringEvent: { freq: 2, count: 12, interval: 1 } }) ); const createdAt = new Date(); Array.from(Array(12).keys()).map(async () => { prismaMock.booking.create.mockResolvedValue(buildBooking({ createdAt })); }); const mockedWebhooks = [ buildWebhook({ subscriberUrl: "http://mockedURL1.com", createdAt, eventTypeId: 1, secret: "secret1", }), buildWebhook({ subscriberUrl: "http://mockedURL2.com", createdAt, eventTypeId: 2, secret: "secret2", }), ]; prismaMock.webhook.findMany.mockResolvedValue(mockedWebhooks); await handler(req, res); const data = JSON.parse(res._getData()); expect(sendPayload).toHaveBeenCalledTimes(24); expect(data.message).toEqual("Bookings created successfully."); expect(data.bookings.length).toEqual(12); }); }); });