Phase 11: Per-Event Reminders (CAL-13/14, NOTIF-04/05/06) #19

Merged
luckberg merged 41 commits from gsd/phase-11-per-event-reminders into main 2026-06-14 14:06:45 -04:00
Showing only changes of commit d18aba7816 - Show all commits
+40
View File
@@ -464,3 +464,43 @@ describe('computeAlertInstantUtc', () => {
expect(result.toISOString()).toBe('2026-11-01T14:00:00.000Z'); expect(result.toISOString()).toBe('2026-11-01T14:00:00.000Z');
}); });
}); });
// ─── Phase 11 Plan 05 WR-01: positive-duration TRIGGER classifies as custom ──
describe('classifyValarms — WR-01: positive-duration trigger (fires after event)', () => {
// RFC 5545 allows TRIGGER:+PT15M — fires 15 min AFTER event start.
// Before the fix: Math.abs() discards the sign and classifies it as preset/offlist 15.
// After the fix: positive duration → {kind:'custom'} (preserve as-is).
it('WR-01: TRIGGER:+PT15M classifies as custom, NOT preset', () => {
// TRIGGER:+PT15M is a post-event alarm; must not be mis-read as a 15-min-before lead.
// This test MUST FAIL before the fix.
const ics = makeIcs(
'BEGIN:VALARM\r\nTRIGGER;RELATED=END:PT15M\r\nACTION:DISPLAY\r\nDESCRIPTION:Reminder\r\nEND:VALARM',
);
expect(classifyValarms(ics)).toEqual({ kind: 'custom' });
});
it('WR-01: TRIGGER:+PT30M classifies as custom, NOT offlist', () => {
// 30 min is in PRESET_MINUTES — without the sign check, Math.abs would give preset/30.
const ics = makeIcs(
'BEGIN:VALARM\r\nTRIGGER:PT30M\r\nACTION:DISPLAY\r\nDESCRIPTION:Reminder\r\nEND:VALARM',
);
expect(classifyValarms(ics)).toEqual({ kind: 'custom' });
});
it('WR-01: negative TRIGGER:-PT15M still classifies as preset (unchanged behavior)', () => {
// Regression guard: negative triggers must not be affected by the fix.
const ics = makeIcs(
'BEGIN:VALARM\r\nTRIGGER:-PT15M\r\nACTION:DISPLAY\r\nDESCRIPTION:Reminder\r\nEND:VALARM',
);
expect(classifyValarms(ics)).toEqual({ kind: 'preset', leadMinutes: 15 });
});
it('WR-01: negative off-list TRIGGER:-PT45M still classifies as offlist', () => {
const ics = makeIcs(
'BEGIN:VALARM\r\nTRIGGER:-PT45M\r\nACTION:DISPLAY\r\nDESCRIPTION:Reminder\r\nEND:VALARM',
);
expect(classifyValarms(ics)).toEqual({ kind: 'offlist', leadMinutes: 45 });
});
});