package nextcloud import ( "strings" "testing" ) func TestParseICSBasics(t *testing.T) { ics := strings.Join([]string{ "BEGIN:VCALENDAR", "VERSION:2.0", "BEGIN:VTIMEZONE", "TZID:Europe/Paris", "BEGIN:STANDARD", "DTSTART:19961027T030000", "END:STANDARD", "END:VTIMEZONE", "BEGIN:VEVENT", "UID:abc-123", "SUMMARY:Réunion\\, équipe", "DESCRIPTION:Ligne 1\\nLigne 2", "DTSTART;TZID=Europe/Paris:20260611T100000", "DTEND;TZID=Europe/Paris:20260611T110000", "RRULE:FREQ=WEEKLY;BYDAY=TH", "EXDATE;TZID=Europe/Paris:20260618T100000", "END:VEVENT", "END:VCALENDAR", }, "\r\n") e := parseICS(ics) if e.UID != "abc-123" { t.Fatalf("UID = %q", e.UID) } if e.Summary != "Réunion, équipe" { t.Fatalf("Summary = %q", e.Summary) } if e.Description != "Ligne 1\nLigne 2" { t.Fatalf("Description = %q", e.Description) } // VTIMEZONE DTSTART must not leak into the event. if e.Start != "20260611T080000Z" { t.Fatalf("Start = %q, want UTC-converted 20260611T080000Z", e.Start) } if e.End != "20260611T090000Z" { t.Fatalf("End = %q", e.End) } if e.AllDay { t.Fatal("AllDay should be false") } if e.RRule != "FREQ=WEEKLY;BYDAY=TH" { t.Fatalf("RRule = %q", e.RRule) } if len(e.ExDates) != 1 || e.ExDates[0] != "20260618T080000Z" { t.Fatalf("ExDates = %v", e.ExDates) } } func TestParseICSAllDayAndFolding(t *testing.T) { ics := strings.Join([]string{ "BEGIN:VCALENDAR", "BEGIN:VEVENT", "UID:x", "SUMMARY:Long title that", " continues folded", "DTSTART;VALUE=DATE:20260611", "DTEND;VALUE=DATE:20260612", "END:VEVENT", "END:VCALENDAR", }, "\r\n") e := parseICS(ics) if !e.AllDay { t.Fatal("AllDay should be true") } if e.Start != "20260611" || e.End != "20260612" { t.Fatalf("Start/End = %q/%q", e.Start, e.End) } if e.Summary != "Long title thatcontinues folded" { t.Fatalf("Summary = %q", e.Summary) } } func TestParseICSPrefersMasterVEvent(t *testing.T) { ics := strings.Join([]string{ "BEGIN:VCALENDAR", "BEGIN:VEVENT", "UID:rec", "RECURRENCE-ID:20260618T100000Z", "SUMMARY:Exception", "DTSTART:20260618T120000Z", "DTEND:20260618T130000Z", "END:VEVENT", "BEGIN:VEVENT", "UID:rec", "SUMMARY:Master", "DTSTART:20260611T100000Z", "DTEND:20260611T110000Z", "RRULE:FREQ=DAILY", "END:VEVENT", "END:VCALENDAR", }, "\r\n") e := parseICS(ics) if e.Summary != "Master" { t.Fatalf("Summary = %q, want master VEVENT", e.Summary) } } func TestBuildICSRoundTrip(t *testing.T) { event := &Event{ UID: "round-trip", Summary: "Point; hebdo, équipe", Description: "Ordre du jour:\npoint 1", Location: "Salle A", Start: "20260611T100000Z", End: "20260611T110000Z", RRule: "FREQ=WEEKLY;BYDAY=TH", ExDates: []string{"20260618T100000Z"}, Color: "#1A73E8", Attendees: []EventAttendee{ {Email: "invite@example.com", Name: "Invité", Status: "ACCEPTED"}, }, } parsed := parseICS(buildICS(event)) if parsed.Summary != event.Summary { t.Fatalf("Summary = %q", parsed.Summary) } if parsed.Description != event.Description { t.Fatalf("Description = %q", parsed.Description) } if parsed.RRule != event.RRule { t.Fatalf("RRule = %q", parsed.RRule) } if parsed.Color != event.Color { t.Fatalf("Color = %q", parsed.Color) } if len(parsed.ExDates) != 1 || parsed.ExDates[0] != "20260618T100000Z" { t.Fatalf("ExDates = %v", parsed.ExDates) } if len(parsed.Attendees) != 1 || parsed.Attendees[0].Email != "invite@example.com" || parsed.Attendees[0].Status != "ACCEPTED" { t.Fatalf("Attendees = %+v", parsed.Attendees) } if parsed.Start != event.Start || parsed.End != event.End { t.Fatalf("Start/End = %q/%q", parsed.Start, parsed.End) } } func TestBuildICSAllDay(t *testing.T) { ics := buildICS(&Event{UID: "ad", Summary: "Férié", Start: "20260714", End: "20260715", AllDay: true}) if !strings.Contains(ics, "DTSTART;VALUE=DATE:20260714") { t.Fatalf("missing all-day DTSTART:\n%s", ics) } parsed := parseICS(ics) if !parsed.AllDay { t.Fatal("AllDay should round-trip") } }