Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 37s
Immutable store ({:events :bookings}) over calendar+availability:
ev/schedule, ev/book, ev/agenda, ev/agenda-for, ev/free?, ev/next-free,
ev/conflicts. Availability queries auto-widen expansion by longest event.
73/73 green. Phase 1 done.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
177 lines
5.2 KiB
Plaintext
177 lines
5.2 KiB
Plaintext
;; 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})))
|