+5![mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)






![Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>](/assets/img/avatar_default.png)

![devin-ai-integration[bot]](/assets/img/avatar_default.png)
devin-ai-integration[bot]
GitHub
Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
morgan@cal.com <morgan@cal.com>
Anik Dhabal Babu
Benny Joo
Sahitya Chandra
Carina Wollendorfer
CarinaWolli
mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
Ayush Kumar
Syed Ali Shahbaz
Lauris Skraucis
supalarry
emrysal
ebeb008f9b
* refactor: convert findQualifiedHostsWithDelegationCredentials to service class with DI - Create QualifiedHostsService class following UserAvailabilityService pattern - Add IQualifiedHostsService interface with prisma and bookingRepo dependencies - Create DI module and container for qualified hosts service - Update filterHostsBySameRoundRobinHost to accept prisma as parameter - Update all usage sites to use the new service: - loadAndValidateUsers.ts - slots/util.ts - test mocks in _post.test.ts - Maintain backward compatibility with original function export - Fix type issues in team properties (rrResetInterval, rrTimestampBasis) Co-Authored-By: morgan@cal.com <morgan@cal.com> * fix: update filterHostsBySameRoundRobinHost test to include prisma parameter - Add missing prisma parameter to all test function calls - Resolves unit test failure caused by function signature change Co-Authored-By: morgan@cal.com <morgan@cal.com> * fix: resolve type issues in FilterHostsService - Import PrismaClient type instead of using unknown - Fix type compatibility for BookingRepository constructor - Update test mocks to use proper BookingRepository type - Ensure all DI dependencies are properly typed Co-Authored-By: morgan@cal.com <morgan@cal.com> * refactor: rename DI files to CamelCase and update imports - Rename all files in packages/lib/di from kebab-case to CamelCase - Update 22 external files with import statements to use new file names - Update internal DI module files with corrected imports - Maintain consistency with TypeScript naming conventions Co-Authored-By: morgan@cal.com <morgan@cal.com> * chore: bump platform libs * chore: bump platform libs * fix: remove obsolete vitest mock after service class refactoring - Remove obsolete mock for old function module - Keep correct mock for new DI container - Resolves CI unit test failures Co-Authored-By: morgan@cal.com <morgan@cal.com> * fix: correct import path for calAIPhone zod-utils module Co-Authored-By: morgan@cal.com <morgan@cal.com> * fix: Booker active booking limit can't be switched off (#23005) * refactor: Get rid of `getServerSideProps` for /getting-started pages (#23003) * refactor * fix type check * fix: Remove Reporting page within Routing Forms (#22990) * fix error in handleNewBooking (#23011) Co-authored-by: CarinaWolli <wollencarina@gmail.com> * Documentation edits made through Mintlify web editor (#23007) Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com> * fix: Contact support button position changed from absolute to fixed (#23002) * feat: Add private links to API (#22943) * --init * address change requests * adding further changes * address feedback * further changes * further clean-up * clean up * fix module import and others * add guards * remove unnecessary comments * remove unnecessary comments * cleanup * sort coderabbig suggestions * improve check * chore: bump platform libraries --------- Co-authored-by: Lauris Skraucis <lauris.skraucis@gmail.com> Co-authored-by: supalarry <laurisskraucis@gmail.com> * chore: release v5.5.15 * chore: bump platform libs * chore: bump platform libs --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: morgan@cal.com <morgan@cal.com> Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> Co-authored-by: Benny Joo <sldisek783@gmail.com> Co-authored-by: Sahitya Chandra <sahityajb@gmail.com> Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com> Co-authored-by: Ayush Kumar <kumarayushkumar@protonmail.com> Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> Co-authored-by: Lauris Skraucis <lauris.skraucis@gmail.com> Co-authored-by: supalarry <laurisskraucis@gmail.com> Co-authored-by: emrysal <me@alexvanandel.com>
302 lines
9.5 KiB
TypeScript
302 lines
9.5 KiB
TypeScript
import {
|
|
getDate,
|
|
getGoogleCalendarCredential,
|
|
createBookingScenario,
|
|
Timezones,
|
|
TestData,
|
|
mockCalendar,
|
|
} from "../../utils/bookingScenario/bookingScenario";
|
|
|
|
import { describe, test } from "vitest";
|
|
|
|
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
|
|
|
|
import { expect, expectedSlotsForSchedule } from "./expects";
|
|
import { setupAndTeardown } from "./setupAndTeardown";
|
|
|
|
describe("getSchedule", () => {
|
|
const availableSlotsService = getAvailableSlotsService();
|
|
setupAndTeardown();
|
|
describe("Calendar event", () => {
|
|
test("correctly identifies unavailable slots from selected calendars at user level", async () => {
|
|
const { dateString: plus1DateString } = getDate({ dateIncrement: 1 });
|
|
const { dateString: plus2DateString } = getDate({ dateIncrement: 2 });
|
|
const useEventLevelSelectedCalendars = false;
|
|
|
|
mockCalendar("googlecalendar", {
|
|
create: {
|
|
uid: "MOCK_ID",
|
|
iCalUID: "MOCKED_GOOGLE_CALENDAR_ICS_ID",
|
|
},
|
|
busySlots: [
|
|
{
|
|
start: `${plus2DateString}T04:45:00.000Z`,
|
|
end: `${plus2DateString}T23:00:00.000Z`,
|
|
},
|
|
],
|
|
});
|
|
|
|
const scenarioData = {
|
|
eventTypes: [
|
|
{
|
|
id: 1,
|
|
useEventLevelSelectedCalendars,
|
|
slotInterval: 45,
|
|
length: 45,
|
|
users: [
|
|
{
|
|
id: 101,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
users: [
|
|
{
|
|
...TestData.users.example,
|
|
id: 101,
|
|
schedules: [TestData.schedules.IstWorkHours],
|
|
credentials: [getGoogleCalendarCredential()],
|
|
selectedCalendars: [TestData.selectedCalendars.google],
|
|
},
|
|
],
|
|
apps: [TestData.apps["google-calendar"]],
|
|
};
|
|
// An event with one accepted booking
|
|
await createBookingScenario(scenarioData);
|
|
|
|
const scheduleForDayWithAGoogleCalendarBooking = await availableSlotsService.getAvailableSlots({
|
|
input: {
|
|
eventTypeId: 1,
|
|
eventTypeSlug: "",
|
|
startTime: `${plus1DateString}T18:30:00.000Z`,
|
|
endTime: `${plus2DateString}T18:29:59.999Z`,
|
|
timeZone: Timezones["+5:30"],
|
|
isTeamEvent: false,
|
|
orgSlug: null,
|
|
},
|
|
});
|
|
|
|
// As per Google Calendar Availability, only 4PM(4-4:45PM) GMT slot would be available
|
|
expect(scheduleForDayWithAGoogleCalendarBooking).toHaveTimeSlots([`04:00:00.000Z`], {
|
|
dateString: plus2DateString,
|
|
});
|
|
});
|
|
|
|
describe("useEventLevelSelectedCalendars is true", () => {
|
|
test("correctly identifies unavailable slots from selected calendars at event level", async () => {
|
|
const { dateString: plus1DateString } = getDate({ dateIncrement: 1 });
|
|
const { dateString: plus2DateString } = getDate({ dateIncrement: 2 });
|
|
const useEventLevelSelectedCalendars = true;
|
|
const eventTypeId = 1;
|
|
mockCalendar("googlecalendar", {
|
|
create: {
|
|
uid: "MOCK_ID",
|
|
iCalUID: "MOCKED_GOOGLE_CALENDAR_ICS_ID",
|
|
},
|
|
busySlots: [
|
|
{
|
|
start: `${plus2DateString}T04:45:00.000Z`,
|
|
end: `${plus2DateString}T23:00:00.000Z`,
|
|
},
|
|
],
|
|
});
|
|
|
|
const scenarioData = {
|
|
eventTypes: [
|
|
{
|
|
id: 1,
|
|
useEventLevelSelectedCalendars,
|
|
slotInterval: 45,
|
|
length: 45,
|
|
users: [
|
|
{
|
|
id: 101,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
users: [
|
|
{
|
|
...TestData.users.example,
|
|
id: 101,
|
|
schedules: [TestData.schedules.IstWorkHours],
|
|
credentials: [getGoogleCalendarCredential()],
|
|
selectedCalendars: [
|
|
{
|
|
...TestData.selectedCalendars.google,
|
|
eventTypeId,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
apps: [TestData.apps["google-calendar"]],
|
|
};
|
|
// An event with one accepted booking
|
|
await createBookingScenario(scenarioData);
|
|
|
|
const scheduleForDayWithAGoogleCalendarBooking = await availableSlotsService.getAvailableSlots({
|
|
input: {
|
|
eventTypeId,
|
|
eventTypeSlug: "",
|
|
startTime: `${plus1DateString}T18:30:00.000Z`,
|
|
endTime: `${plus2DateString}T18:29:59.999Z`,
|
|
timeZone: Timezones["+5:30"],
|
|
isTeamEvent: false,
|
|
orgSlug: null,
|
|
},
|
|
});
|
|
|
|
// As per Google Calendar Availability, only 4PM(4-4:45PM) GMT slot would be available
|
|
expect(scheduleForDayWithAGoogleCalendarBooking).toHaveTimeSlots([`04:00:00.000Z`], {
|
|
dateString: plus2DateString,
|
|
});
|
|
});
|
|
|
|
test("doesn't consider user level selected calendars", async () => {
|
|
const { dateString: plus1DateString } = getDate({ dateIncrement: 1 });
|
|
const { dateString: plus2DateString } = getDate({ dateIncrement: 2 });
|
|
const useEventLevelSelectedCalendars = true;
|
|
const eventTypeId = 1;
|
|
mockCalendar("googlecalendar", {
|
|
create: {
|
|
uid: "MOCK_ID",
|
|
iCalUID: "MOCKED_GOOGLE_CALENDAR_ICS_ID",
|
|
},
|
|
busySlots: [
|
|
{
|
|
start: `${plus2DateString}T04:45:00.000Z`,
|
|
end: `${plus2DateString}T23:00:00.000Z`,
|
|
},
|
|
],
|
|
});
|
|
|
|
const scenarioData = {
|
|
eventTypes: [
|
|
{
|
|
id: 1,
|
|
useEventLevelSelectedCalendars,
|
|
slotInterval: 60,
|
|
length: 60,
|
|
users: [
|
|
{
|
|
id: 101,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
users: [
|
|
{
|
|
...TestData.users.example,
|
|
id: 101,
|
|
schedules: [TestData.schedules.IstWorkHours],
|
|
credentials: [getGoogleCalendarCredential()],
|
|
selectedCalendars: [
|
|
{
|
|
...TestData.selectedCalendars.google,
|
|
eventTypeId: null,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
apps: [TestData.apps["google-calendar"]],
|
|
};
|
|
// An event with one accepted booking
|
|
await createBookingScenario(scenarioData);
|
|
|
|
const scheduleForDayWithAGoogleCalendarBooking = await availableSlotsService.getAvailableSlots({
|
|
input: {
|
|
eventTypeId,
|
|
eventTypeSlug: "",
|
|
startTime: `${plus1DateString}T18:30:00.000Z`,
|
|
endTime: `${plus2DateString}T18:29:59.999Z`,
|
|
timeZone: Timezones["+5:30"],
|
|
isTeamEvent: false,
|
|
orgSlug: null,
|
|
},
|
|
});
|
|
|
|
// As per Google Calendar Availability, only 4PM(4-4:45PM) GMT slot would be available
|
|
expect(scheduleForDayWithAGoogleCalendarBooking).toHaveTimeSlots(
|
|
expectedSlotsForSchedule["IstWorkHours"].interval["1hr"].allPossibleSlotsStartingAt430,
|
|
{
|
|
dateString: plus2DateString,
|
|
}
|
|
);
|
|
});
|
|
|
|
test("doesnt consider another event type's selected calendars", async () => {
|
|
const { dateString: plus1DateString } = getDate({ dateIncrement: 1 });
|
|
const { dateString: plus2DateString } = getDate({ dateIncrement: 2 });
|
|
const useEventLevelSelectedCalendars = true;
|
|
const eventTypeId = 1;
|
|
const anotherEventTypeId = 2;
|
|
mockCalendar("googlecalendar", {
|
|
create: {
|
|
uid: "MOCK_ID",
|
|
iCalUID: "MOCKED_GOOGLE_CALENDAR_ICS_ID",
|
|
},
|
|
busySlots: [
|
|
{
|
|
start: `${plus2DateString}T04:45:00.000Z`,
|
|
end: `${plus2DateString}T23:00:00.000Z`,
|
|
},
|
|
],
|
|
});
|
|
|
|
const scenarioData = {
|
|
eventTypes: [
|
|
{
|
|
id: eventTypeId,
|
|
useEventLevelSelectedCalendars,
|
|
slotInterval: 60,
|
|
length: 60,
|
|
users: [
|
|
{
|
|
id: 101,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
users: [
|
|
{
|
|
...TestData.users.example,
|
|
id: 101,
|
|
schedules: [TestData.schedules.IstWorkHours],
|
|
credentials: [getGoogleCalendarCredential()],
|
|
selectedCalendars: [
|
|
{
|
|
...TestData.selectedCalendars.google,
|
|
eventTypeId: anotherEventTypeId,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
apps: [TestData.apps["google-calendar"]],
|
|
};
|
|
// An event with one accepted booking
|
|
await createBookingScenario(scenarioData);
|
|
|
|
const scheduleForDayWithAGoogleCalendarBooking = await availableSlotsService.getAvailableSlots({
|
|
input: {
|
|
eventTypeId,
|
|
eventTypeSlug: "",
|
|
startTime: `${plus1DateString}T18:30:00.000Z`,
|
|
endTime: `${plus2DateString}T18:29:59.999Z`,
|
|
timeZone: Timezones["+5:30"],
|
|
isTeamEvent: false,
|
|
orgSlug: null,
|
|
},
|
|
});
|
|
|
|
// As per Google Calendar Availability, only 4PM(4-4:45PM) GMT slot would be available
|
|
expect(scheduleForDayWithAGoogleCalendarBooking).toHaveTimeSlots(
|
|
expectedSlotsForSchedule["IstWorkHours"].interval["1hr"].allPossibleSlotsStartingAt430,
|
|
{
|
|
dateString: plus2DateString,
|
|
}
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|