;; lib/events/tests/api.sx — public events facade (schedule/agenda/free/book). (define ev-api-pass 0) (define ev-api-fail 0) (define ev-api-failures (list)) (define ev-api-check! (fn (name got expected) (if (= got expected) (set! ev-api-pass (+ ev-api-pass 1)) (do (set! ev-api-fail (+ ev-api-fail 1)) (append! ev-api-failures (str name "\n expected: " expected "\n got: " got)))))) ;; A store with a weekly yoga class (Mon+Wed 18:00, 60m, 4 occurrences). (define ev-api-store (fn () (ev/schedule (ev/empty) (quote yoga) (ev-dt 2026 6 1 18 0) 60 {:freq :weekly :count 4 :byday (list 0 2)} 20))) (define ev-api-run-all! (fn () (let ((s0 (ev-api-store))) (let ((occs (ev/agenda s0 (ev-date 2026 6 1) (ev-date 2026 7 1)))) (let ((s1 (ev/book (ev/book s0 (quote nia) (ev-occ-key (first occs))) (quote nia) (ev-occ-key (first (rest occs)))))) (do (ev-api-check! "agenda expands weekly class to four occurrences" (map (fn (o) (ev-dt->civil (get o :start))) occs) (list (list 2026 6 1) (list 2026 6 3) (list 2026 6 8) (list 2026 6 10))) (ev-api-check! "empty store has empty agenda" (ev/agenda (ev/empty) (ev-date 2026 6 1) (ev-date 2026 7 1)) (list)) (ev-api-check! "max duration reflects scheduled events" (ev/store-max-duration s0) 60) (ev-api-check! "max duration of empty store is zero" (ev/store-max-duration (ev/empty)) 0) (ev-api-check! "agenda-for lists only booked occurrences" (map (fn (o) (ev-dt->civil (get o :start))) (ev/agenda-for s1 (quote nia) (ev-date 2026 6 1) (ev-date 2026 7 1))) (list (list 2026 6 1) (list 2026 6 3))) (ev-api-check! "agenda-for empty for unbooked actor" (ev/agenda-for s1 (quote zed) (ev-date 2026 6 1) (ev-date 2026 7 1)) (list)) (ev-api-check! "free? false during a booked occurrence" (ev/free? s1 (quote nia) (ev-dt 2026 6 1 18 30) (ev-dt 2026 6 1 19 0)) false) (ev-api-check! "free? true in an open window" (ev/free? s1 (quote nia) (ev-dt 2026 6 1 9 0) (ev-dt 2026 6 1 10 0)) true) (ev-api-check! "free? half-open at occurrence end" (ev/free? s1 (quote nia) (ev-dt 2026 6 1 19 0) (ev-dt 2026 6 1 20 0)) true) (ev-api-check! "free? true for an actor who booked nothing" (ev/free? s1 (quote zed) (ev-dt 2026 6 1 18 0) (ev-dt 2026 6 1 19 0)) true) (ev-api-check! "next-free skips the booked slot to the hour after" (ev-dt-tod (ev/next-free s1 (quote nia) (ev-dt 2026 6 1 18 0) 60 (ev-dt 2026 6 1 23 0))) (* 19 60)) (ev-api-check! "next-free returns `after` when already open" (ev/next-free s1 (quote nia) (ev-dt 2026 6 1 9 0) 60 (ev-dt 2026 6 1 18 0)) (ev-dt 2026 6 1 9 0)) (ev-api-check! "no conflict among disjoint bookings" (ev/has-conflict? s1 (quote nia) (ev-date 2026 6 1) (ev-date 2026 7 1)) false) (let ((sc (ev/book (ev/schedule s1 (quote talk) (ev-dt 2026 6 1 18 30) 60 nil 5) (quote nia) (ev-occ-key (ev-occ (quote talk) (ev-dt 2026 6 1 18 30) 60))))) (ev-api-check! "overlapping second booking creates a conflict" (ev/has-conflict? sc (quote nia) (ev-date 2026 6 1) (ev-date 2026 7 1)) true)))))))) (define ev-api-tests-run! (fn () (do (set! ev-api-pass 0) (set! ev-api-fail 0) (set! ev-api-failures (list)) (ev-api-run-all!) {:failures ev-api-failures :total (+ ev-api-pass ev-api-fail) :passed ev-api-pass :failed ev-api-fail})))