From cc333f7182a21fed3d90e59e4e67615c7ac38b1d Mon Sep 17 00:00:00 2001 From: Lennart K <18233294+lennart-k@users.noreply.github.com> Date: Sat, 31 Jan 2026 14:16:57 +0100 Subject: [PATCH] Add test to make sure that objects from Thunderbird are accepted see #173 --- .../integration_tests/caldav/calendar_put.rs | 50 +++++ .../caldav/resources/ical_thunderbird.ics | 206 ++++++++++++++++++ 2 files changed, 256 insertions(+) create mode 100644 tests/integration_tests/caldav/resources/ical_thunderbird.ics diff --git a/tests/integration_tests/caldav/calendar_put.rs b/tests/integration_tests/caldav/calendar_put.rs index fd1b836..cfad44e 100644 --- a/tests/integration_tests/caldav/calendar_put.rs +++ b/tests/integration_tests/caldav/calendar_put.rs @@ -72,3 +72,53 @@ END:VCALENDAR"; "#); } + +/// Thunderbird creates VTIMEZONE objects with invalid RRULEs. +/// While invalid, we still want to accept them since Thunderbird is quite commonly used. +/// In the future, we might fix invalid timezones ourself. +#[rstest] +#[tokio::test] +async fn test_put_thunderbird( + #[from(test_store_context)] + #[future] + context: TestStoreContext, +) { + let context = context.await; + let app = get_app(context.clone()); + + let calendar_meta = CalendarMetadata { + displayname: Some("Calendar".to_string()), + description: Some("Description".to_string()), + color: Some("#00FF00".to_string()), + order: 0, + }; + let (principal, cal_id) = ("user", "calendar"); + let url = format!("/caldav/principal/{principal}/{cal_id}"); + + let mut request = Request::builder() + .method("MKCALENDAR") + .uri(&url) + .body(Body::from(mkcalendar_template(&calendar_meta))) + .unwrap(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::CREATED); + + let ical = include_str!("resources/ical_thunderbird.ics"); + + let mut request = Request::builder() + .method("PUT") + .uri(format!("{url}/qwue23489.ics")) + .header("If-None-Match", "*") + .header("Content-Type", "text/calendar") + .body(Body::from(ical)) + .unwrap(); + request + .headers_mut() + .typed_insert(Authorization::basic("user", "pass")); + + let response = app.clone().oneshot(request).await.unwrap(); + assert_eq!(response.status(), StatusCode::CREATED); +} diff --git a/tests/integration_tests/caldav/resources/ical_thunderbird.ics b/tests/integration_tests/caldav/resources/ical_thunderbird.ics new file mode 100644 index 0000000..4899388 --- /dev/null +++ b/tests/integration_tests/caldav/resources/ical_thunderbird.ics @@ -0,0 +1,206 @@ +BEGIN:VCALENDAR +PRODID:-//Mozilla.org/NONSGML Mozilla Calendar V1.1//EN +VERSION:2.0 +BEGIN:VTIMEZONE +TZID:Europe/Berlin +X-TZINFO:Europe/Berlin[2025b] +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+005328 +TZNAME:Europe/Berlin(STD) +DTSTART:18930401T000000 +RDATE:18930401T000000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/Berlin(DST) +DTSTART:19160430T230000 +RDATE:19160430T230000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/Berlin(STD) +DTSTART:19161001T010000 +RDATE:19161001T010000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/Berlin(DST) +DTSTART:19170416T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=3MO;UNTIL=19180415T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/Berlin(STD) +DTSTART:19170917T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=3MO;UNTIL=19180916T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/Berlin(DST) +DTSTART:19400401T020000 +RDATE:19400401T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/Berlin(STD) +DTSTART:19421102T030000 +RDATE:19421102T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/Berlin(DST) +DTSTART:19430329T020000 +RDATE:19430329T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/Berlin(DST) +DTSTART:19440403T020000 +RRULE:FREQ=YEARLY;BYMONTH=4;BYDAY=1MO;UNTIL=19450402T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+030000 +TZOFFSETFROM:+020000 +TZNAME:Europe/Berlin(DST) +DTSTART:19450524T020000 +RDATE:19450524T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/Berlin(STD) +DTSTART:19431004T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1MO;UNTIL=19441002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+030000 +TZNAME:Europe/Berlin(DST) +DTSTART:19450924T030000 +RDATE:19450924T030000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/Berlin(STD) +DTSTART:19451118T030000 +RDATE:19451118T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/Berlin(DST) +DTSTART:19460414T020000 +RDATE:19460414T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/Berlin(DST) +DTSTART:19470406T030000 +RDATE:19470406T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+030000 +TZOFFSETFROM:+020000 +TZNAME:Europe/Berlin(DST) +DTSTART:19470511T030000 +RDATE:19470511T030000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/Berlin(STD) +DTSTART:19461007T030000 +RDATE:19461007T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+030000 +TZNAME:Europe/Berlin(DST) +DTSTART:19470629T030000 +RDATE:19470629T030000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/Berlin(DST) +DTSTART:19480418T020000 +RDATE:19480418T020000 +END:DAYLIGHT +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/Berlin(DST) +DTSTART:19490410T020000 +RDATE:19490410T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/Berlin(STD) +DTSTART:19471005T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=1SU;UNTIL=19491002T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/Berlin(DST) +DTSTART:19800406T020000 +RDATE:19800406T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/Berlin(STD) +DTSTART:19800928T030000 +RRULE:FREQ=YEARLY;BYMONTH=9;BYDAY=-1SU;UNTIL=19950924T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:Europe/Berlin(DST) +DTSTART:19810329T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU;UNTIL=19960331T020000 +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:Europe/Berlin(STD) +DTSTART:19961027T030000 +RDATE:19961027T030000 +END:STANDARD +BEGIN:DAYLIGHT +TZOFFSETTO:+020000 +TZOFFSETFROM:+010000 +TZNAME:(DST) +DTSTART:19970330T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETTO:+010000 +TZOFFSETFROM:+020000 +TZNAME:(STD) +DTSTART:19971026T030000 +RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +CREATED:20260130T134029Z +LAST-MODIFIED:20260130T134029Z +DTSTAMP:20260130T134029Z +UID:620c612d-2b03-4fcf-97c7-8fecd6e18565 +DTSTART;TZID=Europe/Berlin:20260130T050000 +DTEND;TZID=Europe/Berlin:20260130T070000 +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR