events: iCalendar import + occurrence-exact round-trip + 19 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 38s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 38s
ical.sx parses VEVENT/VCALENDAR text back into events (ev/ical-lines->event, ev/parse-vcalendar): DTSTART/DURATION/RRULE (ordinal BYDAY, BYMONTHDAY, UNTIL/ COUNT/INTERVAL) + EXDATE/RDATE. Round-trip is occurrence-exact — export->import expands to the identical occurrence set. Completes bidirectional interop. 360/360 green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -180,6 +180,92 @@
|
||||
(list "BEGIN:VCALENDAR" "VERSION:2.0" "END:VCALENDAR"))
|
||||
"BEGIN:VCALENDAR\r\nVERSION:2.0\r\nEND:VCALENDAR"))))
|
||||
|
||||
;; ---- import + round-trip ----
|
||||
|
||||
;; The occurrence starts an event expands to over a fixed window.
|
||||
(define
|
||||
ev-ic-starts
|
||||
(fn
|
||||
(ev)
|
||||
(map (fn (o) (get o :start)) (ev-expand ev (ev-date 2026 1 1) (ev-date 2027 1 1)))))
|
||||
|
||||
;; Round-trip an event through export then import; true if both expand alike.
|
||||
(define
|
||||
ev-ic-roundtrips?
|
||||
(fn
|
||||
(ev)
|
||||
(= (ev-ic-starts ev) (ev-ic-starts (ev/ical-lines->event (ev/event->ical-lines ev))))))
|
||||
|
||||
(define
|
||||
ev-ic-rt-run-all!
|
||||
(fn
|
||||
()
|
||||
(do
|
||||
;; ---- field parsers ----
|
||||
(ev-ic-check! "parse DTSTART" (ev-ical-parse-dt "20260601T180000Z") (ev-dt 2026 6 1 18 0))
|
||||
(ev-ic-check! "parse DURATION PT1H30M" (ev-ical-parse-duration "PT1H30M") 90)
|
||||
(ev-ic-check! "parse DURATION PT1H" (ev-ical-parse-duration "PT1H") 60)
|
||||
(ev-ic-check! "parse DURATION PT30M" (ev-ical-parse-duration "PT30M") 30)
|
||||
(ev-ic-check! "parse plain BYDAY token" (ev-ical-parse-byday-token "MO") 0)
|
||||
(ev-ic-check! "parse ordinal BYDAY token" (ev-ical-parse-byday-token "2TU") {:ord 2 :wd 1})
|
||||
(ev-ic-check! "parse last-weekday BYDAY token" (ev-ical-parse-byday-token "-1FR") {:ord -1 :wd 4})
|
||||
|
||||
;; ---- imported event basic fields ----
|
||||
(let
|
||||
((ev (ev/ical-lines->event (ev/event->ical-lines (ev-event (quote yoga) (ev-dt 2026 6 1 18 0) 90 nil 1)))))
|
||||
(do
|
||||
(ev-ic-check! "imported id is a symbol" (get ev :id) (quote yoga))
|
||||
(ev-ic-check! "imported dtstart" (get ev :dtstart) (ev-dt 2026 6 1 18 0))
|
||||
(ev-ic-check! "imported duration" (get ev :duration) 90)))
|
||||
|
||||
;; ---- round-trips preserve the occurrence set ----
|
||||
(ev-ic-check!
|
||||
"round-trip: one-off event"
|
||||
(ev-ic-roundtrips? (ev-event (quote a) (ev-dt 2026 6 10 14 0) 60 nil 1))
|
||||
true)
|
||||
(ev-ic-check!
|
||||
"round-trip: daily COUNT"
|
||||
(ev-ic-roundtrips? (ev-event (quote a) (ev-dt 2026 6 1 9 0) 30 {:freq :daily :count 5} 1))
|
||||
true)
|
||||
(ev-ic-check!
|
||||
"round-trip: weekly interval/until/byday + exdate + rdate"
|
||||
(ev-ic-roundtrips?
|
||||
(ev-event-full
|
||||
(quote a)
|
||||
(ev-dt 2026 6 1 18 0)
|
||||
90
|
||||
{:freq :weekly :interval 2 :byday (list 0 2) :until (ev-dt 2026 6 30 23 0)}
|
||||
20
|
||||
(list (ev-dt 2026 6 8 18 0))
|
||||
(list (ev-dt 2026 6 20 18 0))))
|
||||
true)
|
||||
(ev-ic-check!
|
||||
"round-trip: monthly nth-weekday"
|
||||
(ev-ic-roundtrips? (ev-event (quote a) (ev-dt 2026 1 13 9 0) 60 {:freq :monthly :byday (list {:ord 2 :wd 1})} 1))
|
||||
true)
|
||||
(ev-ic-check!
|
||||
"round-trip: monthly bymonthday"
|
||||
(ev-ic-roundtrips? (ev-event (quote a) (ev-dt 2026 1 15 9 0) 60 {:freq :monthly :bymonthday (list 15 -1)} 1))
|
||||
true)
|
||||
|
||||
;; ---- parse a VCALENDAR with several events ----
|
||||
(let
|
||||
((cal
|
||||
(ev/events->ical-lines
|
||||
(list
|
||||
(ev-event (quote a) (ev-dt 2026 6 1 9 0) 30 {:freq :daily :count 3} 1)
|
||||
(ev-event (quote b) (ev-dt 2026 6 2 10 0) 60 nil 1)))))
|
||||
(let
|
||||
((events (ev/parse-vcalendar cal)))
|
||||
(do
|
||||
(ev-ic-check! "VCALENDAR parses both events" (len events) 2)
|
||||
(ev-ic-check! "first event id" (get (first events) :id) (quote a))
|
||||
(ev-ic-check! "second event id" (get (first (rest events)) :id) (quote b))
|
||||
(ev-ic-check!
|
||||
"parsed events expand correctly"
|
||||
(ev-ic-starts (first events))
|
||||
(ev-ic-starts (ev-event (quote a) (ev-dt 2026 6 1 9 0) 30 {:freq :daily :count 3} 1)))))))))
|
||||
|
||||
(define
|
||||
ev-ical-tests-run!
|
||||
(fn
|
||||
@@ -189,4 +275,5 @@
|
||||
(set! ev-ic-fail 0)
|
||||
(set! ev-ic-failures (list))
|
||||
(ev-ic-run-all!)
|
||||
(ev-ic-rt-run-all!)
|
||||
{:failures ev-ic-failures :total (+ ev-ic-pass ev-ic-fail) :passed ev-ic-pass :failed ev-ic-fail})))
|
||||
|
||||
Reference in New Issue
Block a user