events: api.sx — public events facade + 14 tests (Phase 1 complete)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 37s
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>
This commit is contained in:
176
lib/events/tests/api.sx
Normal file
176
lib/events/tests/api.sx
Normal file
@@ -0,0 +1,176 @@
|
||||
;; 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})))
|
||||
Reference in New Issue
Block a user