Files
runway/crates/runway-caldav/tests/fixtures/report-calendar-query.xml
T
connor f8e4a497fa Add the CalDAV client and a CLI to drive it
Discovery is the three PROPFINDs RFC 4791 describes rather than a walk
through likely URLs. Queries use time-range, which v1 never did -- it
fetched whole calendars and filtered in the browser on every view change.
Every write states a precondition, so a stale ETag produces a Conflict a
caller can act on instead of silently destroying somebody's edit.

XML goes through quick-xml with namespace resolution. v1 matched prefixes
with six regexes tried in sequence and recompiled inside the loop; there
is a fixture here that is the same document under different prefixes, and
it parses identically.

Protocol parsing is split from transport so it can be tested against
responses recorded from a real Baikal -- including the second propstat
carrying 404s, which is what makes "this calendar has no colour" different
from "this calendar has an empty colour".

Live tests run against a real server, never a mock. tests/baikal/run.sh
starts a container, walks Baikal's install wizard, and runs them; each
test builds and destroys its own collection, so pointing it at a real
server touches nothing that was already there. They cover discovery,
round-trip, stale-ETag conflict, duplicate create, delete, time-range
filtering, a series returned whole with its override, and writing every
synthetic golden fixture to the server and reading it back.

libdav was evaluated first, as planned. Not adopted: its HttpClient trait
is defined over hyper::body::Incoming, so using it means replacing reqwest
everywhere, plus a DNS resolver for service discovery we do not do and a
second XML parser. Its precondition design is where Precondition's shape
comes from. Reasons are recorded in the crate docs.
2026-08-26 15:32:08 -04:00

97 lines
2.3 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<d:multistatus xmlns:d="DAV:" xmlns:s="http://sabredav.org/ns" xmlns:cal="urn:ietf:params:xml:ns:caldav">
<d:response>
<d:href>/dav.php/calendars/alex/personal/allday.ics</d:href>
<d:propstat>
<d:prop>
<d:getetag>&quot;7c9e8a1d2f3b4c5d6e7f8a9b0c1d2e3f&quot;</d:getetag>
<cal:calendar-data>BEGIN:VCALENDAR
VERSION:2.0
PRODID:DAVx5/4.4.8-gplay ical4j/3.2.19 (com.digibites.calendar)
BEGIN:VEVENT
UID:eee51914-187b-40d5-342c-dc80c118438a
STATUS:CONFIRMED
SUMMARY:Check-in
CLASS:PUBLIC
TRANSP:OPAQUE
DTSTART;VALUE=DATE:20250331
DTEND;VALUE=DATE:20250401
RRULE:FREQ=WEEKLY;BYDAY=MO
CREATED:20250902T164854Z
DTSTAMP:20250902T164854Z
LAST-MODIFIED:20250902T164854Z
SEQUENCE:2
BEGIN:VALARM
ACTION:DISPLAY
DESCRIPTION:Retrospective demo retro
TRIGGER:-PT4H
END:VALARM
END:VEVENT
END:VCALENDAR
</cal:calendar-data>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
</d:response>
<d:response>
<d:href>/dav.php/calendars/alex/personal/zoned.ics</d:href>
<d:propstat>
<d:prop>
<d:getetag>&quot;1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d&quot;</d:getetag>
<cal:calendar-data>BEGIN:VCALENDAR
VERSION:2.0
PRODID:DAVx5/4.5.19-gplay ical4j/4.3.0
BEGIN:VEVENT
DTSTAMP:20260811T142404Z
UID:3a21fd46-26c4-85b5-eee3-6d2d2256f8ef
SUMMARY:Briefing
DTSTART;TZID=America/New_York:20260818T083000
DTEND;TZID=America/New_York:20260818T093000
RRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY=TU
STATUS:CONFIRMED
BEGIN:VALARM
TRIGGER:-PT1H
ACTION:DISPLAY
DESCRIPTION:Design
END:VALARM
BEGIN:VALARM
TRIGGER:-PT12H
ACTION:DISPLAY
DESCRIPTION:Design
END:VALARM
END:VEVENT
BEGIN:VTIMEZONE
TZID:America/New_York
BEGIN:STANDARD
TZNAME:EST
TZOFFSETFROM:-0400
TZOFFSETTO:-0500
DTSTART:20071104T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
END:STANDARD
BEGIN:DAYLIGHT
TZNAME:EDT
TZOFFSETFROM:-0500
TZOFFSETTO:-0400
DTSTART:20070311T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
END:DAYLIGHT
END:VTIMEZONE
END:VCALENDAR
</cal:calendar-data>
</d:prop>
<d:status>HTTP/1.1 200 OK</d:status>
</d:propstat>
</d:response>
<d:response>
<d:href>/dav.php/calendars/alex/personal/gone.ics</d:href>
<d:propstat>
<d:prop>
<d:getetag/>
<cal:calendar-data/>
</d:prop>
<d:status>HTTP/1.1 404 Not Found</d:status>
</d:propstat>
</d:response>
</d:multistatus>