;; lib/events/tests/booking-notify.sx — lifecycle notifications from the stream.

(define ev-bn-pass 0)
(define ev-bn-fail 0)
(define ev-bn-failures (list))

(define
  ev-bn-check!
  (fn
    (name got expected)
    (if
      (= got expected)
      (set! ev-bn-pass (+ ev-bn-pass 1))
      (do
        (set! ev-bn-fail (+ ev-bn-fail 1))
        (append!
          ev-bn-failures
          (str name "\n    expected: " expected "\n    got:      " got))))))

(define
  ev-bn-kinds
  (fn
    (notifs)
    (map (fn (n) (list (get n :recipient) (get n :kind))) notifs)))

(define
  ev-bn-run-all!
  (fn
    ()
    (do
      (let
        ((b (persist/open)))
        (do
          (ev/book! b "o" 1 (quote a))
          (ev/waitlist! b "o" 1 (quote x))
          (ev/cancel-promote! b "o" 1 (quote a))
          (let
            ((ns (ev/booking-notifications b "o" (quote yoga))))
            (do
              (ev-bn-check!
                "lifecycle notifications in order"
                (ev-bn-kinds ns)
                (list
                  (list (quote a) :booked)
                  (list (quote x) :waitlisted)
                  (list (quote a) :cancelled)
                  (list (quote x) :promoted)))
              (ev-bn-check!
                "promotion targets the waitlisted actor"
                (map
                  (fn (n) (get n :recipient))
                  (ev/notify-of-kind ns :promoted))
                (list (quote x)))
              (ev-bn-check!
                "a fresh booking is not flagged as a promotion"
                (len (ev/notify-of-kind ns :booked))
                1)
              (ev-bn-check!
                "every notification carries the event label"
                (get (first ns) :event)
                (quote yoga))))))
      (let
        ((b (persist/open)))
        (do
          (ev/hold! b "p" 3 (quote q))
          (ev/confirm! b "p" (quote q))
          (ev-bn-check!
            "hold then confirm notifications"
            (ev-bn-kinds (ev/booking-notifications b "p" (quote gig)))
            (list (list (quote q) :held) (list (quote q) :confirmed)))))
      (let
        ((b (persist/open)))
        (do
          (ev/hold! b "r" 1 (quote q))
          (ev/release! b "r" (quote q))
          (ev-bn-check!
            "hold then release notifications"
            (ev-bn-kinds (ev/booking-notifications b "r" (quote gig)))
            (list (list (quote q) :held) (list (quote q) :released)))))
      (let
        ((b (persist/open)))
        (do
          (ev/book! b "k" 5 (quote a))
          (ev/book! b "k" 5 (quote c))
          (let
            ((ns (ev/booking-notifications b "k" (quote talk))))
            (do
              (ev-bn-check!
                "notification ids are occ-key/seq"
                (map (fn (n) (get n :id)) ns)
                (list "k/1" "k/2"))
              (ev-bn-check!
                "re-deriving yields identical ids (idempotent)"
                (map
                  (fn (n) (get n :id))
                  (ev/booking-notifications b "k" (quote talk)))
                (list "k/1" "k/2"))))))
      (let
        ((b (persist/open)))
        (do
          (ev/book! b "w" 5 (quote a))
          (ev-bn-check!
            "notification projects to (id recipient body)"
            (ev/booking-notify->msg
              (first (ev/booking-notifications b "w" (quote talk))))
            (list
              "w/1"
              (quote a)
              (list :booking-event :booked (quote talk))))))
      (let
        ((b (persist/open)))
        (do
          (ev/book! b "u" 1 (quote a))
          (ev/waitlist! b "u" 1 (quote x))
          (ev/leave-waitlist! b "u" (quote x))
          (ev-bn-check!
            "leaving the waitlist emits no notification"
            (len
              (ev/notify-of-kind
                (ev/booking-notifications b "u" (quote e))
                :left-waitlist))
            0)
          (ev-bn-check!
            "unbooked occurrence has no notifications"
            (ev/booking-notifications b "empty" (quote e))
            (list)))))))

(define
  ev-booking-notify-tests-run!
  (fn
    ()
    (do
      (set! ev-bn-pass 0)
      (set! ev-bn-fail 0)
      (set! ev-bn-failures (list))
      (ev-bn-run-all!)
      {:failures ev-bn-failures :total (+ ev-bn-pass ev-bn-fail) :passed ev-bn-pass :failed ev-bn-fail})))
