-
Notifications
You must be signed in to change notification settings - Fork 8.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Multiple rr-hosts combine to create erroneous availability #18772
Draft
emrysal
wants to merge
16
commits into
main
Choose a base branch
from
bugfix/rr-busy-combinations-turn-some-slots-available
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+512
−168
Draft
Changes from 14 commits
Commits
Show all changes
16 commits
Select commit
Hold shift + click to select a range
11baee3
wip: Open draft PR with passing test (should fail)
emrysal 0e10874
Fix aggregatedAvailability by not merging rr host availability
emrysal abc4b70
Added test for validating fixed host behaviour
emrysal 371c197
Add test for combined rr and fixed hosts
emrysal a72f030
Check date-ranges arent returned multiple times when offered by multi…
emrysal 1d59916
Also sort date ranges
emrysal cf5cdd5
Adding failing test that should result in 5 slots, but returns 7 as i…
emrysal 6407247
Slots returns duplicate slots for overlapping date ranges
emrysal 7d50b48
As mergeOverlappingDateRanges enabled the unified start time, we need…
emrysal 3c8bf92
Do not 'prettify' slots to the same degree if they fall on different …
emrysal 00ab312
Added test to prove an end date before start also works
emrysal 18ea8f8
Use a string instead of number as key
emrysal 60d0043
Merge branch 'main' into bugfix/rr-busy-combinations-turn-some-slots-…
emrysal 0667cec
chore: Deprecate organizerTimeZone, effectively unused
emrysal f6fe7d8
Adjust all dateRanges similarly
emrysal f826a34
Bring back eventTimeZone
emrysal File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
212 changes: 212 additions & 0 deletions
212
packages/core/getAggregatedAvailability/getAggregatedAvailability.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
import { describe, it, expect } from "vitest"; | ||
|
||
import type { Dayjs } from "@calcom/dayjs"; | ||
import dayjs from "@calcom/dayjs"; | ||
|
||
import { getAggregatedAvailability } from "."; | ||
|
||
// Helper to check if a time range overlaps with availability | ||
const isAvailable = (availability: { start: Dayjs; end: Dayjs }[], range: { start: Dayjs; end: Dayjs }) => { | ||
return availability.some(({ start, end }) => { | ||
return start <= range.start && end >= range.end; | ||
}); | ||
}; | ||
|
||
describe("getAggregatedAvailability", () => { | ||
// rr-host availability used to combine into erroneous slots, this confirms it no longer happens | ||
it("should have no host available between 11:00 and 11:30 on January 23, 2025", () => { | ||
const userAvailability = [ | ||
{ | ||
dateRanges: [], | ||
oooExcludedDateRanges: [ | ||
{ start: dayjs("2025-01-23T11:00:00.000Z"), end: dayjs("2025-01-23T11:20:00.000Z") }, | ||
{ start: dayjs("2025-01-23T16:10:00.000Z"), end: dayjs("2025-01-23T16:30:00.000Z") }, | ||
], | ||
user: { isFixed: false }, | ||
}, | ||
{ | ||
dateRanges: [], | ||
oooExcludedDateRanges: [ | ||
{ start: dayjs("2025-01-23T11:15:00.000Z"), end: dayjs("2025-01-23T11:30:00.000Z") }, | ||
{ start: dayjs("2025-01-23T13:20:00.000Z"), end: dayjs("2025-01-23T13:30:00.000Z") }, | ||
], | ||
user: { isFixed: false }, | ||
}, | ||
]; | ||
|
||
const result = getAggregatedAvailability(userAvailability, "ROUND_ROBIN"); | ||
const timeRangeToCheckBusy = { | ||
start: dayjs("2025-01-23T11:00:00.000Z"), | ||
end: dayjs("2025-01-23T11:30:00.000Z"), | ||
}; | ||
|
||
expect(isAvailable(result, timeRangeToCheckBusy)).toBe(false); | ||
|
||
const timeRangeToCheckAvailable = { | ||
start: dayjs("2025-01-23T11:00:00.000Z"), | ||
end: dayjs("2025-01-23T11:20:00.000Z"), | ||
}; | ||
|
||
expect(isAvailable(result, timeRangeToCheckAvailable)).toBe(true); | ||
}); | ||
|
||
it("it returns the right amount of date ranges even if the end time is before the start time", () => { | ||
const userAvailability = [ | ||
{ | ||
dateRanges: [], | ||
oooExcludedDateRanges: [ | ||
{ start: dayjs("2025-01-27T14:00:00.000Z"), end: dayjs("2025-01-27T04:30-05:00") }, | ||
], | ||
user: { isFixed: false }, | ||
}, | ||
{ | ||
dateRanges: [], | ||
oooExcludedDateRanges: [ | ||
{ start: dayjs("2025-01-27T14:00:00.000Z"), end: dayjs("2025-01-27T14:45:00.000Z") }, | ||
], | ||
user: { isFixed: false }, | ||
}, | ||
]; | ||
|
||
const result = getAggregatedAvailability(userAvailability, "ROUND_ROBIN"); | ||
|
||
expect(result).toEqual([ | ||
{ | ||
start: dayjs("2025-01-27T14:00:00.000Z"), | ||
end: dayjs("2025-01-27T09:30:00.000Z"), | ||
}, | ||
{ | ||
start: dayjs("2025-01-27T14:00:00.000Z"), | ||
end: dayjs("2025-01-27T14:45:00.000Z"), | ||
}, | ||
]); | ||
}); | ||
|
||
// validates fixed host behaviour, they all have to be available | ||
it("should only have all fixed hosts available between 11:15 and 11:20 on January 23, 2025", () => { | ||
const userAvailability = [ | ||
{ | ||
dateRanges: [], | ||
oooExcludedDateRanges: [ | ||
{ start: dayjs("2025-01-23T11:00:00.000Z"), end: dayjs("2025-01-23T11:20:00.000Z") }, | ||
{ start: dayjs("2025-01-23T16:10:00.000Z"), end: dayjs("2025-01-23T16:30:00.000Z") }, | ||
], | ||
user: { isFixed: true }, | ||
}, | ||
{ | ||
dateRanges: [], | ||
oooExcludedDateRanges: [ | ||
{ start: dayjs("2025-01-23T11:15:00.000Z"), end: dayjs("2025-01-23T11:30:00.000Z") }, | ||
{ start: dayjs("2025-01-23T13:20:00.000Z"), end: dayjs("2025-01-23T13:30:00.000Z") }, | ||
], | ||
user: { isFixed: true }, | ||
}, | ||
]; | ||
|
||
const result = getAggregatedAvailability(userAvailability, "ROUND_ROBIN"); | ||
const timeRangeToCheckBusy = { | ||
start: dayjs("2025-01-23T11:00:00.000Z"), | ||
end: dayjs("2025-01-23T11:30:00.000Z"), | ||
}; | ||
|
||
expect(isAvailable(result, timeRangeToCheckBusy)).toBe(false); | ||
|
||
expect(result[0].start.format()).toEqual(dayjs("2025-01-23T11:15:00.000Z").format()); | ||
expect(result[0].end.format()).toEqual(dayjs("2025-01-23T11:20:00.000Z").format()); | ||
}); | ||
|
||
// Combines rr hosts and fixed hosts, both fixed and one of the rr hosts has to be available for the whole period | ||
// All fixed user ranges are merged with each rr-host | ||
it("Fixed hosts and at least one rr host available between 11:00-11:30 & 12:30-13:00 on January 23, 2025", () => { | ||
// Both fixed user A and B are available 11:00-11:30 & 12:30-13:00 & 13:15-13:30 | ||
// Only user C (rr) is available 11:00-11:30 and only user D (rr) is available 12:30-13:00 | ||
// No rr users are available 13:15-13:30 and this date range should not be a result. | ||
const userAvailability = [ | ||
{ | ||
dateRanges: [], | ||
oooExcludedDateRanges: [ | ||
{ start: dayjs("2025-01-23T11:00:00.000Z"), end: dayjs("2025-01-23T11:30:00.000Z") }, | ||
{ start: dayjs("2025-01-23T12:30:00.000Z"), end: dayjs("2025-01-23T13:00:00.000Z") }, | ||
{ start: dayjs("2025-01-23T13:15:00.000Z"), end: dayjs("2025-01-23T13:30:00.000Z") }, | ||
{ start: dayjs("2025-01-23T16:10:00.000Z"), end: dayjs("2025-01-23T16:30:00.000Z") }, | ||
], | ||
user: { isFixed: true }, | ||
}, | ||
{ | ||
dateRanges: [], | ||
oooExcludedDateRanges: [ | ||
{ start: dayjs("2025-01-23T11:00:00.000Z"), end: dayjs("2025-01-23T11:30:00.000Z") }, | ||
{ start: dayjs("2025-01-23T12:30:00.000Z"), end: dayjs("2025-01-23T13:00:00.000Z") }, | ||
{ start: dayjs("2025-01-23T13:15:00.000Z"), end: dayjs("2025-01-23T13:30:00.000Z") }, | ||
{ start: dayjs("2025-01-23T13:20:00.000Z"), end: dayjs("2025-01-23T13:30:00.000Z") }, | ||
], | ||
user: { isFixed: true }, | ||
}, | ||
{ | ||
dateRanges: [], | ||
oooExcludedDateRanges: [ | ||
{ start: dayjs("2025-01-23T11:00:00.000Z"), end: dayjs("2025-01-23T11:30:00.000Z") }, | ||
], | ||
user: { isFixed: false }, | ||
}, | ||
{ | ||
dateRanges: [], | ||
oooExcludedDateRanges: [ | ||
{ start: dayjs("2025-01-23T12:30:00.000Z"), end: dayjs("2025-01-23T13:00:00.000Z") }, | ||
], | ||
user: { isFixed: false }, | ||
}, | ||
]; | ||
|
||
const result = getAggregatedAvailability(userAvailability, "ROUND_ROBIN"); | ||
const timeRangeToCheckAvailable = { | ||
start: dayjs("2025-01-23T11:00:00.000Z"), | ||
end: dayjs("2025-01-23T11:30:00.000Z"), | ||
}; | ||
|
||
expect(isAvailable(result, timeRangeToCheckAvailable)).toBe(true); | ||
|
||
expect(result[0].start.format()).toEqual(dayjs("2025-01-23T11:00:00.000Z").format()); | ||
expect(result[0].end.format()).toEqual(dayjs("2025-01-23T11:30:00.000Z").format()); | ||
expect(result[1].start.format()).toEqual(dayjs("2025-01-23T12:30:00.000Z").format()); | ||
expect(result[1].end.format()).toEqual(dayjs("2025-01-23T13:00:00.000Z").format()); | ||
}); | ||
|
||
it("does not duplicate slots when multiple rr-hosts offer the same availability", () => { | ||
const userAvailability = [ | ||
{ | ||
dateRanges: [], | ||
oooExcludedDateRanges: [ | ||
{ start: dayjs("2025-01-23T11:00:00.000Z"), end: dayjs("2025-01-23T11:30:00.000Z") }, | ||
{ start: dayjs("2025-01-23T12:30:00.000Z"), end: dayjs("2025-01-23T13:00:00.000Z") }, | ||
{ start: dayjs("2025-01-23T13:15:00.000Z"), end: dayjs("2025-01-23T13:30:00.000Z") }, | ||
{ start: dayjs("2025-01-23T16:10:00.000Z"), end: dayjs("2025-01-23T16:30:00.000Z") }, | ||
], | ||
user: { isFixed: true }, | ||
}, | ||
{ | ||
dateRanges: [], | ||
oooExcludedDateRanges: [ | ||
{ start: dayjs("2025-01-23T11:00:00.000Z"), end: dayjs("2025-01-23T11:30:00.000Z") }, | ||
], | ||
user: { isFixed: false }, | ||
}, | ||
{ | ||
dateRanges: [], | ||
oooExcludedDateRanges: [ | ||
{ start: dayjs("2025-01-23T11:00:00.000Z"), end: dayjs("2025-01-23T11:30:00.000Z") }, | ||
], | ||
user: { isFixed: false }, | ||
}, | ||
]; | ||
|
||
const result = getAggregatedAvailability(userAvailability, "ROUND_ROBIN"); | ||
const timeRangeToCheckAvailable = { | ||
start: dayjs("2025-01-23T11:00:00.000Z"), | ||
end: dayjs("2025-01-23T11:30:00.000Z"), | ||
}; | ||
|
||
expect(isAvailable(result, timeRangeToCheckAvailable)).toBe(true); | ||
expect(result.length).toBe(1); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is failing on live code