2023-09-30 04:52:32 +00:00
|
|
|
import appStoreMock from "../../../../../tests/libs/__mocks__/app-store";
|
|
|
|
import i18nMock from "../../../../../tests/libs/__mocks__/libServerI18n";
|
|
|
|
import prismock from "../../../../../tests/libs/__mocks__/prisma";
|
|
|
|
|
2023-10-17 11:16:24 +00:00
|
|
|
import type { BookingReference, Attendee, Booking } from "@prisma/client";
|
2023-09-06 19:23:53 +00:00
|
|
|
import type { Prisma } from "@prisma/client";
|
|
|
|
import type { WebhookTriggerEvents } from "@prisma/client";
|
2023-09-30 04:52:32 +00:00
|
|
|
import type Stripe from "stripe";
|
2023-09-06 19:23:53 +00:00
|
|
|
import { v4 as uuidv4 } from "uuid";
|
|
|
|
import "vitest-fetch-mock";
|
|
|
|
|
|
|
|
import { appStoreMetadata } from "@calcom/app-store/appStoreMetaData";
|
2023-10-10 04:16:04 +00:00
|
|
|
import type { getMockRequestDataForBooking } from "@calcom/features/bookings/lib/handleNewBooking/test/lib/getMockRequestDataForBooking";
|
2023-09-30 04:52:32 +00:00
|
|
|
import { handleStripePaymentSuccess } from "@calcom/features/ee/payments/api/webhook";
|
2023-09-30 13:28:52 +00:00
|
|
|
import type { HttpError } from "@calcom/lib/http-error";
|
2023-09-06 19:23:53 +00:00
|
|
|
import logger from "@calcom/lib/logger";
|
2023-10-02 10:51:04 +00:00
|
|
|
import { safeStringify } from "@calcom/lib/safeStringify";
|
2023-09-06 19:23:53 +00:00
|
|
|
import type { SchedulingType } from "@calcom/prisma/enums";
|
|
|
|
import type { BookingStatus } from "@calcom/prisma/enums";
|
2023-10-10 04:16:04 +00:00
|
|
|
import type { AppMeta } from "@calcom/types/App";
|
2023-09-30 04:52:32 +00:00
|
|
|
import type { NewCalendarEventType } from "@calcom/types/Calendar";
|
2023-09-06 19:23:53 +00:00
|
|
|
import type { EventBusyDate } from "@calcom/types/Calendar";
|
2023-09-30 04:52:32 +00:00
|
|
|
|
|
|
|
import { getMockPaymentService } from "./MockPaymentService";
|
2023-09-06 19:23:53 +00:00
|
|
|
|
2023-10-02 10:51:04 +00:00
|
|
|
logger.setSettings({ minLevel: "silly" });
|
|
|
|
const log = logger.getChildLogger({ prefix: ["[bookingScenario]"] });
|
2023-09-06 19:23:53 +00:00
|
|
|
|
|
|
|
type InputWebhook = {
|
|
|
|
appId: string | null;
|
|
|
|
userId?: number | null;
|
|
|
|
teamId?: number | null;
|
|
|
|
eventTypeId?: number;
|
|
|
|
active: boolean;
|
|
|
|
eventTriggers: WebhookTriggerEvents[];
|
|
|
|
subscriberUrl: string;
|
|
|
|
};
|
|
|
|
/**
|
|
|
|
* Data to be mocked
|
|
|
|
*/
|
|
|
|
type ScenarioData = {
|
|
|
|
// hosts: { id: number; eventTypeId?: number; userId?: number; isFixed?: boolean }[];
|
|
|
|
/**
|
|
|
|
* Prisma would return these eventTypes
|
|
|
|
*/
|
|
|
|
eventTypes: InputEventType[];
|
|
|
|
/**
|
|
|
|
* Prisma would return these users
|
|
|
|
*/
|
|
|
|
users: InputUser[];
|
|
|
|
/**
|
|
|
|
* Prisma would return these apps
|
|
|
|
*/
|
2023-10-10 04:16:04 +00:00
|
|
|
apps?: Partial<AppMeta>[];
|
2023-09-06 19:23:53 +00:00
|
|
|
bookings?: InputBooking[];
|
|
|
|
webhooks?: InputWebhook[];
|
|
|
|
};
|
|
|
|
|
2023-10-10 04:16:04 +00:00
|
|
|
type InputCredential = typeof TestData.credentials.google & {
|
|
|
|
id?: number;
|
|
|
|
};
|
2023-09-06 19:23:53 +00:00
|
|
|
|
|
|
|
type InputSelectedCalendar = typeof TestData.selectedCalendars.google;
|
|
|
|
|
2023-10-10 04:16:04 +00:00
|
|
|
type InputUser = Omit<typeof TestData.users.example, "defaultScheduleId"> & {
|
|
|
|
id: number;
|
|
|
|
defaultScheduleId?: number | null;
|
2023-09-06 19:23:53 +00:00
|
|
|
credentials?: InputCredential[];
|
|
|
|
selectedCalendars?: InputSelectedCalendar[];
|
|
|
|
schedules: {
|
2023-10-10 04:16:04 +00:00
|
|
|
// Allows giving id in the input directly so that it can be referenced somewhere else as well
|
|
|
|
id?: number;
|
2023-09-06 19:23:53 +00:00
|
|
|
name: string;
|
|
|
|
availability: {
|
|
|
|
days: number[];
|
|
|
|
startTime: Date;
|
|
|
|
endTime: Date;
|
|
|
|
date: string | null;
|
|
|
|
}[];
|
|
|
|
timeZone: string;
|
|
|
|
}[];
|
2023-09-30 13:28:52 +00:00
|
|
|
destinationCalendar?: Prisma.DestinationCalendarCreateInput;
|
2023-09-06 19:23:53 +00:00
|
|
|
};
|
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
export type InputEventType = {
|
2023-09-06 19:23:53 +00:00
|
|
|
id: number;
|
|
|
|
title?: string;
|
|
|
|
length?: number;
|
|
|
|
offsetStart?: number;
|
|
|
|
slotInterval?: number;
|
|
|
|
minimumBookingNotice?: number;
|
|
|
|
/**
|
|
|
|
* These user ids are `ScenarioData["users"]["id"]`
|
|
|
|
*/
|
|
|
|
users?: { id: number }[];
|
|
|
|
hosts?: { id: number }[];
|
|
|
|
schedulingType?: SchedulingType;
|
|
|
|
beforeEventBuffer?: number;
|
|
|
|
afterEventBuffer?: number;
|
|
|
|
requiresConfirmation?: boolean;
|
2023-09-30 13:28:52 +00:00
|
|
|
destinationCalendar?: Prisma.DestinationCalendarCreateInput;
|
2023-10-10 04:16:04 +00:00
|
|
|
schedule?: InputUser["schedules"][number];
|
|
|
|
} & Partial<Omit<Prisma.EventTypeCreateInput, "users" | "schedule">>;
|
2023-09-06 19:23:53 +00:00
|
|
|
|
2023-10-17 11:16:24 +00:00
|
|
|
type WhiteListedBookingProps = {
|
2023-09-30 04:52:32 +00:00
|
|
|
id?: number;
|
|
|
|
uid?: string;
|
2023-09-06 19:23:53 +00:00
|
|
|
userId?: number;
|
|
|
|
eventTypeId: number;
|
|
|
|
startTime: string;
|
|
|
|
endTime: string;
|
|
|
|
title?: string;
|
|
|
|
status: BookingStatus;
|
|
|
|
attendees?: { email: string }[];
|
2023-10-12 12:29:29 +00:00
|
|
|
references?: (Omit<ReturnType<typeof getMockBookingReference>, "credentialId"> & {
|
|
|
|
// TODO: Make sure that all references start providing credentialId and then remove this intersection of optional credentialId
|
|
|
|
credentialId?: number | null;
|
|
|
|
})[];
|
2023-09-06 19:23:53 +00:00
|
|
|
};
|
|
|
|
|
2023-10-17 11:16:24 +00:00
|
|
|
type InputBooking = Partial<Omit<Booking, keyof WhiteListedBookingProps>> & WhiteListedBookingProps;
|
|
|
|
|
2023-10-10 04:16:04 +00:00
|
|
|
export const Timezones = {
|
2023-09-06 19:23:53 +00:00
|
|
|
"+5:30": "Asia/Kolkata",
|
|
|
|
"+6:00": "Asia/Dhaka",
|
|
|
|
};
|
2023-09-30 04:52:32 +00:00
|
|
|
|
|
|
|
async function addEventTypesToDb(
|
2023-10-10 04:16:04 +00:00
|
|
|
eventTypes: (Omit<
|
|
|
|
Prisma.EventTypeCreateInput,
|
|
|
|
"users" | "worflows" | "destinationCalendar" | "schedule"
|
|
|
|
> & {
|
2023-09-30 04:52:32 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
users?: any[];
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
workflows?: any[];
|
2023-09-30 13:28:52 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
destinationCalendar?: any;
|
2023-10-10 04:16:04 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
schedule?: any;
|
2023-09-30 04:52:32 +00:00
|
|
|
})[]
|
|
|
|
) {
|
2023-10-02 10:51:04 +00:00
|
|
|
log.silly("TestData: Add EventTypes to DB", JSON.stringify(eventTypes));
|
2023-09-30 04:52:32 +00:00
|
|
|
await prismock.eventType.createMany({
|
|
|
|
data: eventTypes,
|
|
|
|
});
|
2023-10-10 04:16:04 +00:00
|
|
|
const allEventTypes = await prismock.eventType.findMany({
|
|
|
|
include: {
|
|
|
|
users: true,
|
|
|
|
workflows: true,
|
|
|
|
destinationCalendar: true,
|
|
|
|
schedule: true,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is a hack to get the relationship of schedule to be established with eventType. Looks like a prismock bug that creating eventType along with schedule.create doesn't establish the relationship.
|
|
|
|
* HACK STARTS
|
|
|
|
*/
|
|
|
|
log.silly("Fixed possible prismock bug by creating schedule separately");
|
|
|
|
for (let i = 0; i < eventTypes.length; i++) {
|
|
|
|
const eventType = eventTypes[i];
|
|
|
|
const createdEventType = allEventTypes[i];
|
|
|
|
|
|
|
|
if (eventType.schedule) {
|
|
|
|
log.silly("TestData: Creating Schedule for EventType", JSON.stringify(eventType));
|
|
|
|
await prismock.schedule.create({
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
data: {
|
|
|
|
...eventType.schedule.create,
|
|
|
|
eventType: {
|
|
|
|
connect: {
|
|
|
|
id: createdEventType.id,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/***
|
|
|
|
* HACK ENDS
|
|
|
|
*/
|
|
|
|
|
2023-10-02 10:51:04 +00:00
|
|
|
log.silly(
|
2023-09-30 13:28:52 +00:00
|
|
|
"TestData: All EventTypes in DB are",
|
|
|
|
JSON.stringify({
|
2023-10-10 04:16:04 +00:00
|
|
|
eventTypes: allEventTypes,
|
2023-09-30 13:28:52 +00:00
|
|
|
})
|
|
|
|
);
|
2023-10-10 04:16:04 +00:00
|
|
|
return allEventTypes;
|
2023-09-30 04:52:32 +00:00
|
|
|
}
|
2023-09-06 19:23:53 +00:00
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
async function addEventTypes(eventTypes: InputEventType[], usersStore: InputUser[]) {
|
2023-09-06 19:23:53 +00:00
|
|
|
const baseEventType = {
|
|
|
|
title: "Base EventType Title",
|
|
|
|
slug: "base-event-type-slug",
|
|
|
|
timeZone: null,
|
|
|
|
beforeEventBuffer: 0,
|
|
|
|
afterEventBuffer: 0,
|
|
|
|
schedulingType: null,
|
2023-09-30 04:52:32 +00:00
|
|
|
length: 15,
|
2023-09-06 19:23:53 +00:00
|
|
|
//TODO: What is the purpose of periodStartDate and periodEndDate? Test these?
|
|
|
|
periodStartDate: new Date("2022-01-21T09:03:48.000Z"),
|
|
|
|
periodEndDate: new Date("2022-01-21T09:03:48.000Z"),
|
|
|
|
periodCountCalendarDays: false,
|
|
|
|
periodDays: 30,
|
|
|
|
seatsPerTimeSlot: null,
|
|
|
|
metadata: {},
|
|
|
|
minimumBookingNotice: 0,
|
|
|
|
offsetStart: 0,
|
|
|
|
};
|
|
|
|
const foundEvents: Record<number, boolean> = {};
|
|
|
|
const eventTypesWithUsers = eventTypes.map((eventType) => {
|
|
|
|
if (!eventType.slotInterval && !eventType.length) {
|
|
|
|
throw new Error("eventTypes[number]: slotInterval or length must be defined");
|
|
|
|
}
|
|
|
|
if (foundEvents[eventType.id]) {
|
|
|
|
throw new Error(`eventTypes[number]: id ${eventType.id} is not unique`);
|
|
|
|
}
|
|
|
|
foundEvents[eventType.id] = true;
|
|
|
|
const users =
|
|
|
|
eventType.users?.map((userWithJustId) => {
|
|
|
|
return usersStore.find((user) => user.id === userWithJustId.id);
|
|
|
|
}) || [];
|
|
|
|
return {
|
|
|
|
...baseEventType,
|
|
|
|
...eventType,
|
|
|
|
workflows: [],
|
|
|
|
users,
|
2023-09-30 13:28:52 +00:00
|
|
|
destinationCalendar: eventType.destinationCalendar
|
|
|
|
? {
|
|
|
|
create: eventType.destinationCalendar,
|
|
|
|
}
|
|
|
|
: eventType.destinationCalendar,
|
2023-10-10 04:16:04 +00:00
|
|
|
schedule: eventType.schedule
|
|
|
|
? {
|
|
|
|
create: {
|
|
|
|
...eventType.schedule,
|
|
|
|
availability: {
|
|
|
|
createMany: {
|
|
|
|
data: eventType.schedule.availability,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
: eventType.schedule,
|
2023-09-06 19:23:53 +00:00
|
|
|
};
|
|
|
|
});
|
2023-10-02 10:51:04 +00:00
|
|
|
log.silly("TestData: Creating EventType", JSON.stringify(eventTypesWithUsers));
|
2023-10-10 04:16:04 +00:00
|
|
|
return await addEventTypesToDb(eventTypesWithUsers);
|
2023-09-30 04:52:32 +00:00
|
|
|
}
|
2023-09-06 19:23:53 +00:00
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
function addBookingReferencesToDB(bookingReferences: Prisma.BookingReferenceCreateManyInput[]) {
|
|
|
|
prismock.bookingReference.createMany({
|
|
|
|
data: bookingReferences,
|
|
|
|
});
|
2023-09-06 19:23:53 +00:00
|
|
|
}
|
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
async function addBookingsToDb(
|
|
|
|
bookings: (Prisma.BookingCreateInput & {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
references: any[];
|
|
|
|
})[]
|
|
|
|
) {
|
2023-10-12 12:29:29 +00:00
|
|
|
log.silly("TestData: Creating Bookings", JSON.stringify(bookings));
|
2023-09-30 04:52:32 +00:00
|
|
|
await prismock.booking.createMany({
|
|
|
|
data: bookings,
|
|
|
|
});
|
2023-10-02 10:51:04 +00:00
|
|
|
log.silly(
|
2023-10-12 12:29:29 +00:00
|
|
|
"TestData: Bookings as in DB",
|
2023-09-30 04:52:32 +00:00
|
|
|
JSON.stringify({
|
|
|
|
bookings: await prismock.booking.findMany({
|
|
|
|
include: {
|
|
|
|
references: true,
|
2023-10-12 12:29:29 +00:00
|
|
|
attendees: true,
|
2023-09-30 04:52:32 +00:00
|
|
|
},
|
|
|
|
}),
|
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
2023-09-06 19:23:53 +00:00
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
async function addBookings(bookings: InputBooking[]) {
|
2023-10-02 10:51:04 +00:00
|
|
|
log.silly("TestData: Creating Bookings", JSON.stringify(bookings));
|
2023-09-30 04:52:32 +00:00
|
|
|
const allBookings = [...bookings].map((booking) => {
|
|
|
|
if (booking.references) {
|
|
|
|
addBookingReferencesToDB(
|
|
|
|
booking.references.map((reference) => {
|
|
|
|
return {
|
|
|
|
...reference,
|
|
|
|
bookingId: booking.id,
|
|
|
|
};
|
|
|
|
})
|
2023-09-06 19:23:53 +00:00
|
|
|
);
|
2023-09-30 04:52:32 +00:00
|
|
|
}
|
|
|
|
return {
|
|
|
|
uid: uuidv4(),
|
|
|
|
workflowReminders: [],
|
|
|
|
references: [],
|
|
|
|
title: "Test Booking Title",
|
|
|
|
...booking,
|
|
|
|
};
|
2023-09-06 19:23:53 +00:00
|
|
|
});
|
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
await addBookingsToDb(
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
allBookings.map((booking) => {
|
|
|
|
const bookingCreate = booking;
|
|
|
|
if (booking.references) {
|
|
|
|
bookingCreate.references = {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
createMany: {
|
|
|
|
data: booking.references,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
2023-10-12 12:29:29 +00:00
|
|
|
if (booking.attendees) {
|
|
|
|
bookingCreate.attendees = {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
createMany: {
|
|
|
|
data: booking.attendees,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
2023-09-30 04:52:32 +00:00
|
|
|
return bookingCreate;
|
2023-09-06 19:23:53 +00:00
|
|
|
})
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
async function addWebhooksToDb(webhooks: any[]) {
|
|
|
|
await prismock.webhook.createMany({
|
|
|
|
data: webhooks,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async function addWebhooks(webhooks: InputWebhook[]) {
|
2023-10-02 10:51:04 +00:00
|
|
|
log.silly("TestData: Creating Webhooks", safeStringify(webhooks));
|
2023-09-30 04:52:32 +00:00
|
|
|
|
|
|
|
await addWebhooksToDb(webhooks);
|
|
|
|
}
|
|
|
|
|
|
|
|
async function addUsersToDb(users: (Prisma.UserCreateInput & { schedules: Prisma.ScheduleCreateInput[] })[]) {
|
2023-10-02 10:51:04 +00:00
|
|
|
log.silly("TestData: Creating Users", JSON.stringify(users));
|
2023-09-30 04:52:32 +00:00
|
|
|
await prismock.user.createMany({
|
|
|
|
data: users,
|
2023-09-06 19:23:53 +00:00
|
|
|
});
|
2023-10-10 04:16:04 +00:00
|
|
|
|
2023-10-02 10:51:04 +00:00
|
|
|
log.silly(
|
|
|
|
"Added users to Db",
|
|
|
|
safeStringify({
|
2023-10-10 04:16:04 +00:00
|
|
|
allUsers: await prismock.user.findMany({
|
|
|
|
include: {
|
|
|
|
credentials: true,
|
|
|
|
schedules: {
|
|
|
|
include: {
|
|
|
|
availability: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
destinationCalendar: true,
|
|
|
|
},
|
|
|
|
}),
|
2023-10-02 10:51:04 +00:00
|
|
|
})
|
|
|
|
);
|
2023-09-30 04:52:32 +00:00
|
|
|
}
|
2023-09-06 19:23:53 +00:00
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
async function addUsers(users: InputUser[]) {
|
|
|
|
const prismaUsersCreate = users.map((user) => {
|
|
|
|
const newUser = user;
|
|
|
|
if (user.schedules) {
|
|
|
|
newUser.schedules = {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
// @ts-ignore
|
|
|
|
createMany: {
|
|
|
|
data: user.schedules.map((schedule) => {
|
|
|
|
return {
|
|
|
|
...schedule,
|
|
|
|
availability: {
|
|
|
|
createMany: {
|
|
|
|
data: schedule.availability,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}),
|
|
|
|
},
|
2023-09-06 19:23:53 +00:00
|
|
|
};
|
2023-09-30 04:52:32 +00:00
|
|
|
}
|
|
|
|
if (user.credentials) {
|
|
|
|
newUser.credentials = {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
createMany: {
|
|
|
|
data: user.credentials,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
2023-09-30 13:28:52 +00:00
|
|
|
if (user.selectedCalendars) {
|
|
|
|
newUser.selectedCalendars = {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
createMany: {
|
|
|
|
data: user.selectedCalendars,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
2023-09-30 04:52:32 +00:00
|
|
|
return newUser;
|
|
|
|
});
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
await addUsersToDb(prismaUsersCreate);
|
2023-09-06 19:23:53 +00:00
|
|
|
}
|
|
|
|
|
2023-10-10 04:16:04 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
async function addAppsToDb(apps: any[]) {
|
|
|
|
log.silly("TestData: Creating Apps", JSON.stringify({ apps }));
|
|
|
|
await prismock.app.createMany({
|
|
|
|
data: apps,
|
|
|
|
});
|
|
|
|
const allApps = await prismock.app.findMany();
|
|
|
|
log.silly("TestData: Apps as in DB", JSON.stringify({ apps: allApps }));
|
|
|
|
}
|
2023-09-06 19:23:53 +00:00
|
|
|
export async function createBookingScenario(data: ScenarioData) {
|
2023-10-02 10:51:04 +00:00
|
|
|
log.silly("TestData: Creating Scenario", JSON.stringify({ data }));
|
2023-09-30 04:52:32 +00:00
|
|
|
await addUsers(data.users);
|
2023-09-06 19:23:53 +00:00
|
|
|
if (data.apps) {
|
2023-10-10 04:16:04 +00:00
|
|
|
await addAppsToDb(
|
|
|
|
data.apps.map((app) => {
|
|
|
|
// Enable the app by default
|
|
|
|
return { enabled: true, ...app };
|
|
|
|
})
|
|
|
|
);
|
2023-09-06 19:23:53 +00:00
|
|
|
}
|
2023-10-10 04:16:04 +00:00
|
|
|
const eventTypes = await addEventTypes(data.eventTypes, data.users);
|
|
|
|
|
2023-09-06 19:23:53 +00:00
|
|
|
data.bookings = data.bookings || [];
|
2023-09-30 04:52:32 +00:00
|
|
|
// allowSuccessfulBookingCreation();
|
|
|
|
await addBookings(data.bookings);
|
2023-09-06 19:23:53 +00:00
|
|
|
// mockBusyCalendarTimes([]);
|
2023-09-30 04:52:32 +00:00
|
|
|
await addWebhooks(data.webhooks || []);
|
|
|
|
// addPaymentMock();
|
2023-09-06 19:23:53 +00:00
|
|
|
return {
|
2023-10-10 04:16:04 +00:00
|
|
|
eventTypes,
|
2023-09-06 19:23:53 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
// async function addPaymentsToDb(payments: Prisma.PaymentCreateInput[]) {
|
|
|
|
// await prismaMock.payment.createMany({
|
|
|
|
// data: payments,
|
|
|
|
// });
|
|
|
|
// }
|
|
|
|
|
2023-09-06 19:23:53 +00:00
|
|
|
/**
|
|
|
|
* This fn indents to /ally compute day, month, year for the purpose of testing.
|
|
|
|
* We are not using DayJS because that's actually being tested by this code.
|
|
|
|
* - `dateIncrement` adds the increment to current day
|
|
|
|
* - `monthIncrement` adds the increment to current month
|
|
|
|
* - `yearIncrement` adds the increment to current year
|
|
|
|
*/
|
|
|
|
export const getDate = (
|
|
|
|
param: { dateIncrement?: number; monthIncrement?: number; yearIncrement?: number } = {}
|
|
|
|
) => {
|
|
|
|
let { dateIncrement, monthIncrement, yearIncrement } = param;
|
|
|
|
dateIncrement = dateIncrement || 0;
|
|
|
|
monthIncrement = monthIncrement || 0;
|
|
|
|
yearIncrement = yearIncrement || 0;
|
|
|
|
|
|
|
|
let _date = new Date().getDate() + dateIncrement;
|
|
|
|
let year = new Date().getFullYear() + yearIncrement;
|
|
|
|
|
|
|
|
// Make it start with 1 to match with DayJS requiremet
|
|
|
|
let _month = new Date().getMonth() + monthIncrement + 1;
|
|
|
|
|
|
|
|
// If last day of the month(As _month is plus 1 already it is going to be the 0th day of next month which is the last day of current month)
|
|
|
|
const lastDayOfMonth = new Date(year, _month, 0).getDate();
|
|
|
|
const numberOfDaysForNextMonth = +_date - +lastDayOfMonth;
|
|
|
|
if (numberOfDaysForNextMonth > 0) {
|
|
|
|
_date = numberOfDaysForNextMonth;
|
|
|
|
_month = _month + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_month === 13) {
|
|
|
|
_month = 1;
|
|
|
|
year = year + 1;
|
|
|
|
}
|
|
|
|
|
2023-10-03 18:52:19 +00:00
|
|
|
const date = _date < 10 ? `0${_date}` : _date;
|
|
|
|
const month = _month < 10 ? `0${_month}` : _month;
|
2023-09-06 19:23:53 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
date,
|
|
|
|
month,
|
|
|
|
year,
|
|
|
|
dateString: `${year}-${month}-${date}`,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
export function getMockedCredential({
|
|
|
|
metadataLookupKey,
|
|
|
|
key,
|
|
|
|
}: {
|
|
|
|
metadataLookupKey: string;
|
|
|
|
key: {
|
|
|
|
expiry_date?: number;
|
|
|
|
token_type?: string;
|
|
|
|
access_token?: string;
|
|
|
|
refresh_token?: string;
|
|
|
|
scope: string;
|
|
|
|
};
|
|
|
|
}) {
|
2023-09-30 04:52:32 +00:00
|
|
|
const app = appStoreMetadata[metadataLookupKey as keyof typeof appStoreMetadata];
|
2023-09-06 19:23:53 +00:00
|
|
|
return {
|
2023-09-30 04:52:32 +00:00
|
|
|
type: app.type,
|
|
|
|
appId: app.slug,
|
|
|
|
app: app,
|
2023-09-06 19:23:53 +00:00
|
|
|
key: {
|
|
|
|
expiry_date: Date.now() + 1000000,
|
|
|
|
token_type: "Bearer",
|
|
|
|
access_token: "ACCESS_TOKEN",
|
|
|
|
refresh_token: "REFRESH_TOKEN",
|
|
|
|
...key,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getGoogleCalendarCredential() {
|
|
|
|
return getMockedCredential({
|
|
|
|
metadataLookupKey: "googlecalendar",
|
|
|
|
key: {
|
|
|
|
scope:
|
|
|
|
"https://www.googleapis.com/auth/calendar.events https://www.googleapis.com/auth/calendar.readonly",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getZoomAppCredential() {
|
|
|
|
return getMockedCredential({
|
|
|
|
metadataLookupKey: "zoomvideo",
|
|
|
|
key: {
|
2023-09-30 04:52:32 +00:00
|
|
|
scope: "meeting:write",
|
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getStripeAppCredential() {
|
|
|
|
return getMockedCredential({
|
|
|
|
metadataLookupKey: "stripepayment",
|
|
|
|
key: {
|
|
|
|
scope: "read_write",
|
2023-09-06 19:23:53 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export const TestData = {
|
|
|
|
selectedCalendars: {
|
|
|
|
google: {
|
|
|
|
integration: "google_calendar",
|
|
|
|
externalId: "john@example.com",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
credentials: {
|
|
|
|
google: getGoogleCalendarCredential(),
|
|
|
|
},
|
|
|
|
schedules: {
|
|
|
|
IstWorkHours: {
|
|
|
|
name: "9:30AM to 6PM in India - 4:00AM to 12:30PM in GMT",
|
|
|
|
availability: [
|
|
|
|
{
|
2023-10-10 04:16:04 +00:00
|
|
|
// userId: null,
|
|
|
|
// eventTypeId: null,
|
2023-09-06 19:23:53 +00:00
|
|
|
days: [0, 1, 2, 3, 4, 5, 6],
|
|
|
|
startTime: new Date("1970-01-01T09:30:00.000Z"),
|
|
|
|
endTime: new Date("1970-01-01T18:00:00.000Z"),
|
|
|
|
date: null,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
timeZone: Timezones["+5:30"],
|
|
|
|
},
|
2023-10-10 04:16:04 +00:00
|
|
|
/**
|
|
|
|
* Has an overlap with IstEveningShift from 5PM to 6PM IST(11:30AM to 12:30PM GMT)
|
|
|
|
*/
|
|
|
|
IstMorningShift: {
|
|
|
|
name: "9:30AM to 6PM in India - 4:00AM to 12:30PM in GMT",
|
|
|
|
availability: [
|
|
|
|
{
|
|
|
|
// userId: null,
|
|
|
|
// eventTypeId: null,
|
|
|
|
days: [0, 1, 2, 3, 4, 5, 6],
|
|
|
|
startTime: new Date("1970-01-01T09:30:00.000Z"),
|
|
|
|
endTime: new Date("1970-01-01T18:00:00.000Z"),
|
|
|
|
date: null,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
timeZone: Timezones["+5:30"],
|
|
|
|
},
|
|
|
|
/**
|
|
|
|
* Has an overlap with IstMorningShift from 5PM to 6PM IST(11:30AM to 12:30PM GMT)
|
|
|
|
*/
|
|
|
|
IstEveningShift: {
|
|
|
|
name: "5:00PM to 10PM in India - 11:30AM to 16:30PM in GMT",
|
|
|
|
availability: [
|
|
|
|
{
|
|
|
|
// userId: null,
|
|
|
|
// eventTypeId: null,
|
|
|
|
days: [0, 1, 2, 3, 4, 5, 6],
|
|
|
|
startTime: new Date("1970-01-01T17:00:00.000Z"),
|
|
|
|
endTime: new Date("1970-01-01T22:00:00.000Z"),
|
|
|
|
date: null,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
timeZone: Timezones["+5:30"],
|
|
|
|
},
|
2023-09-06 19:23:53 +00:00
|
|
|
IstWorkHoursWithDateOverride: (dateString: string) => ({
|
|
|
|
name: "9:30AM to 6PM in India - 4:00AM to 12:30PM in GMT but with a Date Override for 2PM to 6PM IST(in GST time it is 8:30AM to 12:30PM)",
|
|
|
|
availability: [
|
|
|
|
{
|
|
|
|
days: [0, 1, 2, 3, 4, 5, 6],
|
|
|
|
startTime: new Date("1970-01-01T09:30:00.000Z"),
|
|
|
|
endTime: new Date("1970-01-01T18:00:00.000Z"),
|
|
|
|
date: null,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
days: [0, 1, 2, 3, 4, 5, 6],
|
|
|
|
startTime: new Date(`1970-01-01T14:00:00.000Z`),
|
|
|
|
endTime: new Date(`1970-01-01T18:00:00.000Z`),
|
|
|
|
date: dateString,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
timeZone: Timezones["+5:30"],
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
users: {
|
|
|
|
example: {
|
|
|
|
name: "Example",
|
|
|
|
email: "example@example.com",
|
|
|
|
username: "example",
|
|
|
|
defaultScheduleId: 1,
|
|
|
|
timeZone: Timezones["+5:30"],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
apps: {
|
|
|
|
"google-calendar": {
|
2023-10-10 04:16:04 +00:00
|
|
|
...appStoreMetadata.googlecalendar,
|
2023-09-06 19:23:53 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
keys: {
|
|
|
|
expiry_date: Infinity,
|
|
|
|
client_id: "client_id",
|
|
|
|
client_secret: "client_secret",
|
|
|
|
redirect_uris: ["http://localhost:3000/auth/callback"],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"daily-video": {
|
2023-10-10 04:16:04 +00:00
|
|
|
...appStoreMetadata.dailyvideo,
|
2023-09-30 04:52:32 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
keys: {
|
|
|
|
expiry_date: Infinity,
|
|
|
|
api_key: "",
|
|
|
|
scale_plan: "false",
|
|
|
|
client_id: "client_id",
|
|
|
|
client_secret: "client_secret",
|
|
|
|
redirect_uris: ["http://localhost:3000/auth/callback"],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
zoomvideo: {
|
2023-10-10 04:16:04 +00:00
|
|
|
...appStoreMetadata.zoomvideo,
|
2023-09-30 04:52:32 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
keys: {
|
|
|
|
expiry_date: Infinity,
|
|
|
|
api_key: "",
|
|
|
|
scale_plan: "false",
|
|
|
|
client_id: "client_id",
|
|
|
|
client_secret: "client_secret",
|
|
|
|
redirect_uris: ["http://localhost:3000/auth/callback"],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
"stripe-payment": {
|
2023-10-10 04:16:04 +00:00
|
|
|
...appStoreMetadata.stripepayment,
|
2023-09-06 19:23:53 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
keys: {
|
|
|
|
expiry_date: Infinity,
|
|
|
|
api_key: "",
|
|
|
|
scale_plan: "false",
|
|
|
|
client_id: "client_id",
|
|
|
|
client_secret: "client_secret",
|
|
|
|
redirect_uris: ["http://localhost:3000/auth/callback"],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
export class MockError extends Error {
|
|
|
|
constructor(message: string) {
|
|
|
|
super(message);
|
|
|
|
this.name = "MockError";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getOrganizer({
|
|
|
|
name,
|
|
|
|
email,
|
|
|
|
id,
|
|
|
|
schedules,
|
|
|
|
credentials,
|
|
|
|
selectedCalendars,
|
2023-09-30 13:28:52 +00:00
|
|
|
destinationCalendar,
|
2023-10-10 04:16:04 +00:00
|
|
|
defaultScheduleId,
|
2023-09-06 19:23:53 +00:00
|
|
|
}: {
|
|
|
|
name: string;
|
|
|
|
email: string;
|
|
|
|
id: number;
|
|
|
|
schedules: InputUser["schedules"];
|
|
|
|
credentials?: InputCredential[];
|
|
|
|
selectedCalendars?: InputSelectedCalendar[];
|
2023-10-10 04:16:04 +00:00
|
|
|
defaultScheduleId?: number | null;
|
2023-09-30 13:28:52 +00:00
|
|
|
destinationCalendar?: Prisma.DestinationCalendarCreateInput;
|
2023-09-06 19:23:53 +00:00
|
|
|
}) {
|
|
|
|
return {
|
|
|
|
...TestData.users.example,
|
|
|
|
name,
|
|
|
|
email,
|
|
|
|
id,
|
|
|
|
schedules,
|
|
|
|
credentials,
|
|
|
|
selectedCalendars,
|
2023-09-30 13:28:52 +00:00
|
|
|
destinationCalendar,
|
2023-10-10 04:16:04 +00:00
|
|
|
defaultScheduleId,
|
2023-09-06 19:23:53 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getScenarioData({
|
|
|
|
organizer,
|
|
|
|
eventTypes,
|
|
|
|
usersApartFromOrganizer = [],
|
|
|
|
apps = [],
|
|
|
|
webhooks,
|
2023-09-30 04:52:32 +00:00
|
|
|
bookings,
|
2023-09-06 19:23:53 +00:00
|
|
|
}: // hosts = [],
|
|
|
|
{
|
|
|
|
organizer: ReturnType<typeof getOrganizer>;
|
|
|
|
eventTypes: ScenarioData["eventTypes"];
|
2023-09-30 13:28:52 +00:00
|
|
|
apps?: ScenarioData["apps"];
|
2023-09-06 19:23:53 +00:00
|
|
|
usersApartFromOrganizer?: ScenarioData["users"];
|
|
|
|
webhooks?: ScenarioData["webhooks"];
|
2023-09-30 04:52:32 +00:00
|
|
|
bookings?: ScenarioData["bookings"];
|
2023-09-06 19:23:53 +00:00
|
|
|
// hosts?: ScenarioData["hosts"];
|
|
|
|
}) {
|
|
|
|
const users = [organizer, ...usersApartFromOrganizer];
|
|
|
|
eventTypes.forEach((eventType) => {
|
|
|
|
if (
|
|
|
|
eventType.users?.filter((eventTypeUser) => {
|
|
|
|
return !users.find((userToCreate) => userToCreate.id === eventTypeUser.id);
|
|
|
|
}).length
|
|
|
|
) {
|
|
|
|
throw new Error(`EventType ${eventType.id} has users that are not present in ScenarioData["users"]`);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return {
|
|
|
|
// hosts: [...hosts],
|
2023-09-30 04:52:32 +00:00
|
|
|
eventTypes: eventTypes.map((eventType, index) => {
|
|
|
|
return {
|
|
|
|
...eventType,
|
|
|
|
title: `Test Event Type - ${index + 1}`,
|
|
|
|
description: `It's a test event type - ${index + 1}`,
|
|
|
|
};
|
|
|
|
}),
|
2023-09-30 13:28:52 +00:00
|
|
|
users: users.map((user) => {
|
|
|
|
const newUser = {
|
|
|
|
...user,
|
|
|
|
};
|
|
|
|
if (user.destinationCalendar) {
|
|
|
|
newUser.destinationCalendar = {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
// @ts-ignore
|
|
|
|
create: user.destinationCalendar,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
return newUser;
|
|
|
|
}),
|
2023-09-06 19:23:53 +00:00
|
|
|
apps: [...apps],
|
|
|
|
webhooks,
|
2023-09-30 04:52:32 +00:00
|
|
|
bookings: bookings || [],
|
2023-09-06 19:23:53 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
export function enableEmailFeature() {
|
|
|
|
prismock.feature.create({
|
|
|
|
data: {
|
2023-09-06 19:23:53 +00:00
|
|
|
slug: "emails",
|
|
|
|
enabled: false,
|
2023-09-30 04:52:32 +00:00
|
|
|
type: "KILL_SWITCH",
|
2023-09-06 19:23:53 +00:00
|
|
|
},
|
2023-09-30 04:52:32 +00:00
|
|
|
});
|
2023-09-06 19:23:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function mockNoTranslations() {
|
2023-10-02 10:51:04 +00:00
|
|
|
log.silly("Mocking i18n.getTranslation to return identity function");
|
2023-09-19 21:02:57 +00:00
|
|
|
// @ts-expect-error FIXME
|
2023-09-06 19:23:53 +00:00
|
|
|
i18nMock.getTranslation.mockImplementation(() => {
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
const identityFn = (key: string) => key;
|
|
|
|
resolve(identityFn);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-09-30 13:28:52 +00:00
|
|
|
/**
|
|
|
|
* @param metadataLookupKey
|
|
|
|
* @param calendarData Specify uids and other data to be faked to be returned by createEvent and updateEvent
|
|
|
|
*/
|
|
|
|
export function mockCalendar(
|
2023-09-30 04:52:32 +00:00
|
|
|
metadataLookupKey: keyof typeof appStoreMetadata,
|
|
|
|
calendarData?: {
|
2023-09-30 13:28:52 +00:00
|
|
|
create?: {
|
2023-10-02 10:51:04 +00:00
|
|
|
id?: string;
|
2023-09-30 13:28:52 +00:00
|
|
|
uid?: string;
|
2023-10-02 10:51:04 +00:00
|
|
|
iCalUID?: string;
|
2023-09-30 04:52:32 +00:00
|
|
|
};
|
|
|
|
update?: {
|
2023-10-02 10:51:04 +00:00
|
|
|
id?: string;
|
2023-09-30 04:52:32 +00:00
|
|
|
uid: string;
|
2023-10-02 10:51:04 +00:00
|
|
|
iCalUID?: string;
|
2023-09-30 04:52:32 +00:00
|
|
|
};
|
2023-09-30 13:28:52 +00:00
|
|
|
busySlots?: { start: `${string}Z`; end: `${string}Z` }[];
|
|
|
|
creationCrash?: boolean;
|
|
|
|
updationCrash?: boolean;
|
|
|
|
getAvailabilityCrash?: boolean;
|
2023-09-30 04:52:32 +00:00
|
|
|
}
|
|
|
|
) {
|
2023-09-06 19:23:53 +00:00
|
|
|
const appStoreLookupKey = metadataLookupKey;
|
2023-09-30 04:52:32 +00:00
|
|
|
const normalizedCalendarData = calendarData || {
|
|
|
|
create: {
|
|
|
|
uid: "MOCK_ID",
|
|
|
|
},
|
|
|
|
update: {
|
|
|
|
uid: "UPDATED_MOCK_ID",
|
|
|
|
},
|
|
|
|
};
|
2023-10-02 10:51:04 +00:00
|
|
|
log.silly(`Mocking ${appStoreLookupKey} on appStoreMock`);
|
2023-09-30 04:52:32 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
const createEventCalls: any[] = [];
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
const updateEventCalls: any[] = [];
|
2023-10-12 12:29:29 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
const deleteEventCalls: any[] = [];
|
2023-09-30 04:52:32 +00:00
|
|
|
const app = appStoreMetadata[metadataLookupKey as keyof typeof appStoreMetadata];
|
2023-09-06 19:23:53 +00:00
|
|
|
appStoreMock.default[appStoreLookupKey as keyof typeof appStoreMock.default].mockResolvedValue({
|
|
|
|
lib: {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
CalendarService: function MockCalendarService() {
|
|
|
|
return {
|
2023-09-30 04:52:32 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
createEvent: async function (...rest: any[]): Promise<NewCalendarEventType> {
|
2023-09-30 13:28:52 +00:00
|
|
|
if (calendarData?.creationCrash) {
|
|
|
|
throw new Error("MockCalendarService.createEvent fake error");
|
|
|
|
}
|
2023-09-30 04:52:32 +00:00
|
|
|
const [calEvent, credentialId] = rest;
|
2023-10-02 10:51:04 +00:00
|
|
|
log.silly("mockCalendar.createEvent", JSON.stringify({ calEvent, credentialId }));
|
2023-09-30 04:52:32 +00:00
|
|
|
createEventCalls.push(rest);
|
2023-09-06 19:23:53 +00:00
|
|
|
return Promise.resolve({
|
2023-09-30 04:52:32 +00:00
|
|
|
type: app.type,
|
|
|
|
additionalInfo: {},
|
|
|
|
uid: "PROBABLY_UNUSED_UID",
|
2023-10-02 10:51:04 +00:00
|
|
|
// A Calendar is always expected to return an id.
|
|
|
|
id: normalizedCalendarData.create?.id || "FALLBACK_MOCK_CALENDAR_EVENT_ID",
|
|
|
|
iCalUID: normalizedCalendarData.create?.iCalUID,
|
2023-09-30 04:52:32 +00:00
|
|
|
// Password and URL seems useless for CalendarService, plan to remove them if that's the case
|
|
|
|
password: "MOCK_PASSWORD",
|
|
|
|
url: "https://UNUSED_URL",
|
|
|
|
});
|
|
|
|
},
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
updateEvent: async function (...rest: any[]): Promise<NewCalendarEventType> {
|
2023-09-30 13:28:52 +00:00
|
|
|
if (calendarData?.updationCrash) {
|
|
|
|
throw new Error("MockCalendarService.updateEvent fake error");
|
|
|
|
}
|
2023-09-30 04:52:32 +00:00
|
|
|
const [uid, event, externalCalendarId] = rest;
|
2023-10-02 10:51:04 +00:00
|
|
|
log.silly("mockCalendar.updateEvent", JSON.stringify({ uid, event, externalCalendarId }));
|
2023-09-30 04:52:32 +00:00
|
|
|
// eslint-disable-next-line prefer-rest-params
|
|
|
|
updateEventCalls.push(rest);
|
|
|
|
return Promise.resolve({
|
|
|
|
type: app.type,
|
|
|
|
additionalInfo: {},
|
|
|
|
uid: "PROBABLY_UNUSED_UID",
|
2023-10-02 10:51:04 +00:00
|
|
|
iCalUID: normalizedCalendarData.update?.iCalUID,
|
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
2023-09-30 13:28:52 +00:00
|
|
|
id: normalizedCalendarData.update?.uid || "FALLBACK_MOCK_ID",
|
2023-09-30 04:52:32 +00:00
|
|
|
// Password and URL seems useless for CalendarService, plan to remove them if that's the case
|
|
|
|
password: "MOCK_PASSWORD",
|
|
|
|
url: "https://UNUSED_URL",
|
2023-09-06 19:23:53 +00:00
|
|
|
});
|
|
|
|
},
|
2023-10-12 12:29:29 +00:00
|
|
|
deleteEvent: async (...rest: any[]) => {
|
|
|
|
log.silly("mockCalendar.deleteEvent", JSON.stringify({ rest }));
|
|
|
|
// eslint-disable-next-line prefer-rest-params
|
|
|
|
deleteEventCalls.push(rest);
|
|
|
|
},
|
2023-09-30 13:28:52 +00:00
|
|
|
getAvailability: async (): Promise<EventBusyDate[]> => {
|
|
|
|
if (calendarData?.getAvailabilityCrash) {
|
|
|
|
throw new Error("MockCalendarService.getAvailability fake error");
|
|
|
|
}
|
2023-09-06 19:23:53 +00:00
|
|
|
return new Promise((resolve) => {
|
2023-09-30 13:28:52 +00:00
|
|
|
resolve(calendarData?.busySlots || []);
|
2023-09-06 19:23:53 +00:00
|
|
|
});
|
|
|
|
},
|
|
|
|
};
|
|
|
|
},
|
|
|
|
},
|
|
|
|
});
|
2023-09-30 04:52:32 +00:00
|
|
|
return {
|
|
|
|
createEventCalls,
|
2023-10-12 12:29:29 +00:00
|
|
|
deleteEventCalls,
|
2023-09-30 04:52:32 +00:00
|
|
|
updateEventCalls,
|
|
|
|
};
|
2023-09-06 19:23:53 +00:00
|
|
|
}
|
|
|
|
|
2023-09-30 13:28:52 +00:00
|
|
|
export function mockCalendarToHaveNoBusySlots(
|
|
|
|
metadataLookupKey: keyof typeof appStoreMetadata,
|
2023-10-02 10:51:04 +00:00
|
|
|
calendarData?: Parameters<typeof mockCalendar>[1]
|
2023-09-30 13:28:52 +00:00
|
|
|
) {
|
|
|
|
calendarData = calendarData || {
|
|
|
|
create: {
|
|
|
|
uid: "MOCK_ID",
|
|
|
|
},
|
|
|
|
update: {
|
|
|
|
uid: "UPDATED_MOCK_ID",
|
|
|
|
},
|
|
|
|
};
|
|
|
|
return mockCalendar(metadataLookupKey, { ...calendarData, busySlots: [] });
|
|
|
|
}
|
|
|
|
|
|
|
|
export function mockCalendarToCrashOnCreateEvent(metadataLookupKey: keyof typeof appStoreMetadata) {
|
|
|
|
return mockCalendar(metadataLookupKey, { creationCrash: true });
|
|
|
|
}
|
|
|
|
|
|
|
|
export function mockCalendarToCrashOnUpdateEvent(metadataLookupKey: keyof typeof appStoreMetadata) {
|
|
|
|
return mockCalendar(metadataLookupKey, { updationCrash: true });
|
|
|
|
}
|
|
|
|
|
|
|
|
export function mockVideoApp({
|
2023-09-06 19:23:53 +00:00
|
|
|
metadataLookupKey,
|
|
|
|
appStoreLookupKey,
|
2023-09-30 04:52:32 +00:00
|
|
|
videoMeetingData,
|
2023-09-30 13:28:52 +00:00
|
|
|
creationCrash,
|
|
|
|
updationCrash,
|
2023-09-06 19:23:53 +00:00
|
|
|
}: {
|
|
|
|
metadataLookupKey: string;
|
|
|
|
appStoreLookupKey?: string;
|
2023-09-30 04:52:32 +00:00
|
|
|
videoMeetingData?: {
|
|
|
|
password: string;
|
|
|
|
id: string;
|
|
|
|
url: string;
|
|
|
|
};
|
2023-09-30 13:28:52 +00:00
|
|
|
creationCrash?: boolean;
|
|
|
|
updationCrash?: boolean;
|
2023-09-06 19:23:53 +00:00
|
|
|
}) {
|
|
|
|
appStoreLookupKey = appStoreLookupKey || metadataLookupKey;
|
2023-09-30 04:52:32 +00:00
|
|
|
videoMeetingData = videoMeetingData || {
|
|
|
|
id: "MOCK_ID",
|
|
|
|
password: "MOCK_PASS",
|
|
|
|
url: `http://mock-${metadataLookupKey}.example.com`,
|
|
|
|
};
|
2023-10-12 12:29:29 +00:00
|
|
|
log.silly("mockVideoApp", JSON.stringify({ metadataLookupKey, appStoreLookupKey }));
|
2023-10-10 04:16:04 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2023-09-30 04:52:32 +00:00
|
|
|
const createMeetingCalls: any[] = [];
|
2023-10-10 04:16:04 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
2023-09-30 04:52:32 +00:00
|
|
|
const updateMeetingCalls: any[] = [];
|
2023-10-12 12:29:29 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
const deleteMeetingCalls: any[] = [];
|
2023-09-06 19:23:53 +00:00
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
appStoreMock.default[appStoreLookupKey as keyof typeof appStoreMock.default].mockImplementation(() => {
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
resolve({
|
|
|
|
lib: {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
2023-10-10 04:16:04 +00:00
|
|
|
VideoApiAdapter: (credential) => {
|
|
|
|
return {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
createMeeting: (...rest: any[]) => {
|
|
|
|
if (creationCrash) {
|
|
|
|
throw new Error("MockVideoApiAdapter.createMeeting fake error");
|
|
|
|
}
|
|
|
|
createMeetingCalls.push({
|
|
|
|
credential,
|
|
|
|
args: rest,
|
|
|
|
});
|
|
|
|
|
|
|
|
return Promise.resolve({
|
|
|
|
type: appStoreMetadata[metadataLookupKey as keyof typeof appStoreMetadata].type,
|
|
|
|
...videoMeetingData,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
updateMeeting: async (...rest: any[]) => {
|
|
|
|
if (updationCrash) {
|
|
|
|
throw new Error("MockVideoApiAdapter.updateMeeting fake error");
|
|
|
|
}
|
|
|
|
const [bookingRef, calEvent] = rest;
|
|
|
|
updateMeetingCalls.push({
|
|
|
|
credential,
|
|
|
|
args: rest,
|
|
|
|
});
|
|
|
|
if (!bookingRef.type) {
|
|
|
|
throw new Error("bookingRef.type is not defined");
|
|
|
|
}
|
|
|
|
if (!calEvent.organizer) {
|
|
|
|
throw new Error("calEvent.organizer is not defined");
|
|
|
|
}
|
2023-10-12 12:29:29 +00:00
|
|
|
log.silly("MockVideoApiAdapter.updateMeeting", JSON.stringify({ bookingRef, calEvent }));
|
2023-10-10 04:16:04 +00:00
|
|
|
return Promise.resolve({
|
|
|
|
type: appStoreMetadata[metadataLookupKey as keyof typeof appStoreMetadata].type,
|
|
|
|
...videoMeetingData,
|
|
|
|
});
|
|
|
|
},
|
2023-10-12 12:29:29 +00:00
|
|
|
deleteMeeting: async (...rest: any[]) => {
|
|
|
|
log.silly("MockVideoApiAdapter.deleteMeeting", JSON.stringify(rest));
|
|
|
|
deleteMeetingCalls.push({
|
|
|
|
credential,
|
|
|
|
args: rest,
|
|
|
|
});
|
|
|
|
},
|
2023-10-10 04:16:04 +00:00
|
|
|
};
|
|
|
|
},
|
2023-09-06 19:23:53 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2023-09-30 04:52:32 +00:00
|
|
|
return {
|
|
|
|
createMeetingCalls,
|
|
|
|
updateMeetingCalls,
|
2023-10-12 12:29:29 +00:00
|
|
|
deleteMeetingCalls,
|
2023-09-30 04:52:32 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-09-30 13:28:52 +00:00
|
|
|
export function mockSuccessfulVideoMeetingCreation({
|
|
|
|
metadataLookupKey,
|
|
|
|
appStoreLookupKey,
|
|
|
|
videoMeetingData,
|
|
|
|
}: {
|
|
|
|
metadataLookupKey: string;
|
|
|
|
appStoreLookupKey?: string;
|
|
|
|
videoMeetingData?: {
|
|
|
|
password: string;
|
|
|
|
id: string;
|
|
|
|
url: string;
|
|
|
|
};
|
|
|
|
}) {
|
|
|
|
return mockVideoApp({
|
|
|
|
metadataLookupKey,
|
|
|
|
appStoreLookupKey,
|
|
|
|
videoMeetingData,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function mockVideoAppToCrashOnCreateMeeting({
|
|
|
|
metadataLookupKey,
|
|
|
|
appStoreLookupKey,
|
|
|
|
}: {
|
|
|
|
metadataLookupKey: string;
|
|
|
|
appStoreLookupKey?: string;
|
|
|
|
}) {
|
|
|
|
return mockVideoApp({
|
|
|
|
metadataLookupKey,
|
|
|
|
appStoreLookupKey,
|
|
|
|
creationCrash: true,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
export function mockPaymentApp({
|
|
|
|
metadataLookupKey,
|
|
|
|
appStoreLookupKey,
|
|
|
|
}: {
|
|
|
|
metadataLookupKey: string;
|
|
|
|
appStoreLookupKey?: string;
|
|
|
|
}) {
|
|
|
|
appStoreLookupKey = appStoreLookupKey || metadataLookupKey;
|
|
|
|
const { paymentUid, externalId, MockPaymentService } = getMockPaymentService();
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
appStoreMock.default[appStoreLookupKey as keyof typeof appStoreMock.default].mockImplementation(() => {
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
resolve({
|
|
|
|
lib: {
|
|
|
|
PaymentService: MockPaymentService,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
paymentUid,
|
|
|
|
externalId,
|
|
|
|
};
|
2023-09-06 19:23:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export function mockErrorOnVideoMeetingCreation({
|
|
|
|
metadataLookupKey,
|
|
|
|
appStoreLookupKey,
|
|
|
|
}: {
|
|
|
|
metadataLookupKey: string;
|
|
|
|
appStoreLookupKey?: string;
|
|
|
|
}) {
|
|
|
|
appStoreLookupKey = appStoreLookupKey || metadataLookupKey;
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
appStoreMock.default[appStoreLookupKey].mockImplementation(() => {
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
resolve({
|
|
|
|
lib: {
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
//@ts-ignore
|
|
|
|
VideoApiAdapter: () => ({
|
|
|
|
createMeeting: () => {
|
|
|
|
throw new MockError("Error creating Video meeting");
|
|
|
|
},
|
|
|
|
}),
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getBooker({ name, email }: { name: string; email: string }) {
|
|
|
|
return {
|
|
|
|
name,
|
|
|
|
email,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
export function getMockedStripePaymentEvent({ paymentIntentId }: { paymentIntentId: string }) {
|
|
|
|
return {
|
|
|
|
id: null,
|
|
|
|
data: {
|
|
|
|
object: {
|
|
|
|
id: paymentIntentId,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
} as unknown as Stripe.Event;
|
2023-09-06 19:23:53 +00:00
|
|
|
}
|
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
export async function mockPaymentSuccessWebhookFromStripe({ externalId }: { externalId: string }) {
|
|
|
|
let webhookResponse = null;
|
|
|
|
try {
|
|
|
|
await handleStripePaymentSuccess(getMockedStripePaymentEvent({ paymentIntentId: externalId }));
|
|
|
|
} catch (e) {
|
2023-10-02 10:51:04 +00:00
|
|
|
log.silly("mockPaymentSuccessWebhookFromStripe:catch", JSON.stringify(e));
|
2023-09-30 13:28:52 +00:00
|
|
|
|
2023-09-30 04:52:32 +00:00
|
|
|
webhookResponse = e as HttpError;
|
|
|
|
}
|
|
|
|
return { webhookResponse };
|
|
|
|
}
|
2023-10-10 04:16:04 +00:00
|
|
|
|
|
|
|
export function getExpectedCalEventForBookingRequest({
|
|
|
|
bookingRequest,
|
|
|
|
eventType,
|
|
|
|
}: {
|
|
|
|
bookingRequest: ReturnType<typeof getMockRequestDataForBooking>;
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
eventType: any;
|
|
|
|
}) {
|
|
|
|
return {
|
|
|
|
// keep adding more fields as needed, so that they can be verified in all scenarios
|
|
|
|
type: eventType.title,
|
|
|
|
// Not sure why, but milliseconds are missing in cal Event.
|
|
|
|
startTime: bookingRequest.start.replace(".000Z", "Z"),
|
|
|
|
endTime: bookingRequest.end.replace(".000Z", "Z"),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-10-12 12:29:29 +00:00
|
|
|
export function getMockBookingReference(
|
|
|
|
bookingReference: Partial<BookingReference> & Pick<BookingReference, "type" | "uid" | "credentialId">
|
|
|
|
) {
|
|
|
|
let credentialId = bookingReference.credentialId;
|
|
|
|
if (bookingReference.type === appStoreMetadata.dailyvideo.type) {
|
|
|
|
// Right now we seems to be storing credentialId for `dailyvideo` in BookingReference as null. Another possible value is 0 in there.
|
|
|
|
credentialId = null;
|
|
|
|
log.debug("Ensuring null credentialId for dailyvideo");
|
|
|
|
}
|
|
|
|
return {
|
|
|
|
...bookingReference,
|
|
|
|
credentialId,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
export function getMockBookingAttendee(attendee: Omit<Attendee, "bookingId">) {
|
|
|
|
return {
|
|
|
|
id: attendee.id,
|
|
|
|
timeZone: attendee.timeZone,
|
|
|
|
name: attendee.name,
|
|
|
|
email: attendee.email,
|
|
|
|
locale: attendee.locale,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-10-10 04:16:04 +00:00
|
|
|
export const enum BookingLocations {
|
|
|
|
CalVideo = "integrations:daily",
|
|
|
|
ZoomVideo = "integrations:zoom",
|
|
|
|
}
|
2023-10-17 11:16:24 +00:00
|
|
|
|
|
|
|
const getMockAppStatus = ({
|
|
|
|
slug,
|
|
|
|
failures,
|
|
|
|
success,
|
|
|
|
}: {
|
|
|
|
slug: string;
|
|
|
|
failures: number;
|
|
|
|
success: number;
|
|
|
|
}) => {
|
|
|
|
const foundEntry = Object.entries(appStoreMetadata).find(([, app]) => {
|
|
|
|
return app.slug === slug;
|
|
|
|
});
|
|
|
|
if (!foundEntry) {
|
|
|
|
throw new Error("App not found for the slug");
|
|
|
|
}
|
|
|
|
const foundApp = foundEntry[1];
|
|
|
|
return {
|
|
|
|
appName: foundApp.slug,
|
|
|
|
type: foundApp.type,
|
|
|
|
failures,
|
|
|
|
success,
|
|
|
|
errors: [],
|
|
|
|
};
|
|
|
|
};
|
|
|
|
export const getMockFailingAppStatus = ({ slug }: { slug: string }) => {
|
|
|
|
return getMockAppStatus({ slug, failures: 1, success: 0 });
|
|
|
|
};
|
|
|
|
|
|
|
|
export const getMockPassingAppStatus = ({ slug }: { slug: string }) => {
|
|
|
|
return getMockAppStatus({ slug, failures: 0, success: 1 });
|
|
|
|
};
|