Skip to content

Commit 272d97c

Browse files
authored
fix: prevent 500 errors in round-robin scheduling from OOO calibration for single host (calcom#25369)
* wip * add unit test
1 parent f2b2b6e commit 272d97c

2 files changed

Lines changed: 74 additions & 0 deletions

File tree

packages/features/bookings/lib/getLuckyUser.test.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,75 @@ describe("maximize availability and weights", () => {
827827
);
828828
});
829829

830+
it("skips OOO calibration when there is only one host", async () => {
831+
const users: GetLuckyUserAvailableUsersType = [
832+
buildUser({
833+
id: 1,
834+
username: "test1",
835+
name: "Test User 1",
836+
email: "test1@example.com",
837+
bookings: [],
838+
}),
839+
];
840+
841+
const allRRHosts = [
842+
{
843+
user: {
844+
id: users[0].id,
845+
email: users[0].email,
846+
credentials: [],
847+
userLevelSelectedCalendars: [],
848+
},
849+
weight: users[0].weight,
850+
createdAt: new Date(0),
851+
},
852+
];
853+
854+
CalendarManagerMock.getBusyCalendarTimes.mockResolvedValue({ success: true, data: [] });
855+
856+
// Mock OOO entry for the single host
857+
prismaMock.outOfOfficeEntry.findMany.mockResolvedValue([
858+
{
859+
start: dayjs().subtract(10, "day").toDate(),
860+
end: dayjs().subtract(5, "day").toDate(),
861+
userId: users[0].id,
862+
},
863+
]);
864+
865+
prismaMock.user.findMany.mockResolvedValue(users);
866+
prismaMock.host.findMany.mockResolvedValue([
867+
{
868+
userId: allRRHosts[0].user.id,
869+
weight: allRRHosts[0].weight,
870+
createdAt: allRRHosts[0].createdAt,
871+
},
872+
]);
873+
874+
// Mock some bookings during the OOO period (though there's only one host)
875+
prismaMock.booking.findMany.mockResolvedValue([
876+
buildBooking({
877+
id: 1,
878+
userId: 1,
879+
createdAt: dayjs().subtract(7, "days").toDate(),
880+
}),
881+
]);
882+
883+
// Should return the only available user without throwing division by zero error
884+
await expect(
885+
luckyUserService.getLuckyUser({
886+
availableUsers: users,
887+
eventType: {
888+
id: 1,
889+
isRRWeightsEnabled: true,
890+
team: { rrResetInterval: RRResetInterval.MONTH, rrTimestampBasis: RRTimestampBasis.CREATED_AT },
891+
includeNoShowInRRCalculation: false,
892+
},
893+
allRRHosts,
894+
routingFormResponse: null,
895+
})
896+
).resolves.toStrictEqual(users[0]);
897+
});
898+
830899
it("applies calibration to newly added hosts so they are not penalized unfairly compared to their peers", async () => {
831900
const users: GetLuckyUserAvailableUsersType = [
832901
buildUser({

packages/features/bookings/lib/getLuckyUser.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,11 @@ export class LuckyUserService implements ILuckyUserService {
300300
const oooCalibration = new Map<number, number>();
301301

302302
oooData.forEach(({ userId, oooEntries }) => {
303+
// Skip OOO calibration if there's only one host (division by zero would occur)
304+
if (hosts.length <= 1) {
305+
return;
306+
}
307+
303308
let calibration = 0;
304309

305310
oooEntries.forEach((oooEntry) => {

0 commit comments

Comments
 (0)