using Socialize.Api.Modules.CalendarIntegrations.Services; namespace Socialize.Tests.CalendarIntegrations; public class IcsCalendarParserTests { private readonly IcsCalendarParser _parser = new(); [Fact] public void Parse_preserves_all_day_calendar_dates() { string ics = """ BEGIN:VCALENDAR BEGIN:VEVENT UID:christmas-eve SUMMARY:Christmas Eve DTSTART;VALUE=DATE:20261224 DTEND;VALUE=DATE:20261225 END:VEVENT END:VCALENDAR """; ParsedCalendarEvent calendarEvent = Assert.Single(_parser.Parse( ics, new DateOnly(2026, 12, 1), new DateOnly(2026, 12, 31))); Assert.True(calendarEvent.IsAllDay); Assert.Equal(new DateOnly(2026, 12, 24), calendarEvent.StartDate); Assert.Equal(new DateOnly(2026, 12, 25), calendarEvent.EndDate); Assert.Null(calendarEvent.StartUtc); } [Fact] public void Parse_keeps_floating_timed_events_as_local_values_without_utc_conversion() { string ics = """ BEGIN:VCALENDAR BEGIN:VEVENT UID:floating SUMMARY:Local planning DTSTART:20260510T090000 DTEND:20260510T100000 END:VEVENT END:VCALENDAR """; ParsedCalendarEvent calendarEvent = Assert.Single(_parser.Parse( ics, new DateOnly(2026, 5, 1), new DateOnly(2026, 5, 31))); Assert.False(calendarEvent.IsAllDay); Assert.True(calendarEvent.IsFloatingTime); Assert.Equal(new DateTime(2026, 5, 10, 9, 0, 0), calendarEvent.StartLocalDateTime); Assert.Null(calendarEvent.StartUtc); } [Fact] public void Parse_converts_timezone_bearing_timed_events_when_timezone_is_known() { string ics = """ BEGIN:VCALENDAR BEGIN:VEVENT UID:timed SUMMARY:Launch DTSTART;TZID=America/Toronto:20260510T090000 DTEND;TZID=America/Toronto:20260510T100000 END:VEVENT END:VCALENDAR """; ParsedCalendarEvent calendarEvent = Assert.Single(_parser.Parse( ics, new DateOnly(2026, 5, 1), new DateOnly(2026, 5, 31))); Assert.False(calendarEvent.IsFloatingTime); Assert.Equal("America/Toronto", calendarEvent.TimeZoneId); Assert.Equal(TimeSpan.Zero, calendarEvent.StartUtc?.Offset); Assert.Equal(13, calendarEvent.StartUtc?.Hour); } [Fact] public void Parse_expands_yearly_recurrence_inside_requested_range() { string ics = """ BEGIN:VCALENDAR BEGIN:VEVENT UID:mothers-day SUMMARY:Mother's Day DTSTART;VALUE=DATE:20240512 DTEND;VALUE=DATE:20240513 RRULE:FREQ=YEARLY;COUNT=5 END:VEVENT END:VCALENDAR """; IReadOnlyCollection events = _parser.Parse( ics, new DateOnly(2026, 1, 1), new DateOnly(2027, 12, 31)); Assert.Collection( events, first => Assert.Equal(new DateOnly(2026, 5, 12), first.StartDate), second => Assert.Equal(new DateOnly(2027, 5, 12), second.StartDate)); Assert.All(events, calendarEvent => Assert.Equal("mothers-day", calendarEvent.RecurrenceId)); } [Fact] public void Parse_unfolds_folded_text_lines() { string ics = """ BEGIN:VCALENDAR BEGIN:VEVENT UID:folded SUMMARY:Long title DTSTART;VALUE=DATE:20260510 END:VEVENT END:VCALENDAR """; ParsedCalendarEvent calendarEvent = Assert.Single(_parser.Parse( ics, new DateOnly(2026, 5, 1), new DateOnly(2026, 5, 31))); Assert.Equal("Longtitle", calendarEvent.Title); } }