fix(02): correct DURATION-only event end in expand.ts (BUG 1)
- Replace dtend ?? dtstart with event.endDate which handles DURATION-only VEVENTs - Add positive-duration guard (PT30M / P1D) to both non-recurring and recurring branches - Add single-duration.ics fixture and regression test asserting end > start for DURATION-only events
This commit is contained in:
@@ -140,6 +140,42 @@ describe('expandOccurrences', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('Non-recurring DURATION-only event — single-duration.ics', () => {
|
||||
it('BUG-1 regression: non-recurring event with DURATION but no DTEND has end strictly after start', () => {
|
||||
// Real Fastmail events use DURATION (not DTEND). Before the fix, the NON-RECURRING branch
|
||||
// used getFirstPropertyValue('dtend') ?? dtstart, which returned dtstart when DTEND was absent,
|
||||
// producing zero-duration occurrences (invisible in week/day views).
|
||||
const rawVevent = loadFixture('single-duration.ics')
|
||||
const windowStart = new Date('2026-06-01T00:00:00Z')
|
||||
const windowEnd = new Date('2026-07-01T00:00:00Z')
|
||||
|
||||
const occurrences = expandOccurrences(
|
||||
rawVevent,
|
||||
windowStart,
|
||||
windowEnd,
|
||||
1,
|
||||
'My Calendar',
|
||||
1,
|
||||
'#4A90D9',
|
||||
false,
|
||||
)
|
||||
|
||||
expect(occurrences.length).toBe(1)
|
||||
|
||||
const occ = occurrences[0]
|
||||
expect(occ.allDay).toBe(false)
|
||||
|
||||
// Parse both via Temporal.ZonedDateTime and assert end > start
|
||||
const startZdt = Temporal.ZonedDateTime.from(occ.start)
|
||||
const endZdt = Temporal.ZonedDateTime.from(occ.end)
|
||||
expect(Temporal.ZonedDateTime.compare(endZdt, startZdt)).toBeGreaterThan(0)
|
||||
|
||||
// Verify the actual duration is correct: DURATION:PT1H → end is 1 hour after start
|
||||
expect(endZdt.hour - startZdt.hour).toBe(1)
|
||||
expect(endZdt.minute).toBe(startZdt.minute)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Cross-contract: expand output → Temporal.ZonedDateTime.from (regression guard)', () => {
|
||||
it('timed event start/end strings from weekly-dst.ics parse via Temporal.ZonedDateTime.from without throwing', () => {
|
||||
// This is the integration test that was missing. It takes the actual serializeTime output
|
||||
|
||||
Reference in New Issue
Block a user