;; lib/events/tests/notify.sx — durable notification delivery flows.

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

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

;; Each case runs a Scheme flow program (notify flows preloaded) and asserts on
;; the SX-native result. Scheme symbols come back as strings.
(define
  ev-nt-run-all!
  (fn
    ()
    (do
      (ev-nt-check!
        "reminder delivers on the first attempt"
        (ev/notify-run
          "(define s (flow/start (ev-deliver-reminder 3) (list (quote m1) (quote alice) (quote hello))))\n           (flow-run-host (lambda (k p) (list (quote ok) (quote sent))) 5)\n           (list (flow/status (car (cdr s))) (flow/result (car (cdr s))))")
        (list "done" (list "delivered" "m1" 1)))
      (ev-nt-check!
        "reminder retries a transient failure then delivers"
        (ev/notify-run
          "(define hits 0)\n           (define s (flow/start (ev-deliver-reminder 3) (list (quote m1) (quote bob) (quote hi))))\n           (flow-run-host (lambda (k p) (begin (set! hits (+ hits 1)) (if (< hits 2) (list (quote retry) (quote down)) (list (quote ok) (quote sent))))) 10)\n           (list (flow/result (car (cdr s))) hits)")
        (list (list "delivered" "m1" 2) 2))
      (ev-nt-check!
        "reminder gives up after maxn attempts"
        (ev/notify-run
          "(define s (flow/start (ev-deliver-reminder 2) (list (quote m1) (quote x) (quote y))))\n           (flow-run-host (lambda (k p) (list (quote retry) (quote down))) 10)\n           (flow/result (car (cdr s)))")
        (list "failed" "m1" "down"))
      (ev-nt-check!
        "redelivery of the same id sends only once (at-least-once, idempotent)"
        (ev/notify-run
          "(define sent (list)) (define deliveries 0)\n           (define (xport k p)\n             (let ((id (ev-msg-id p)))\n               (if (ev-mem id sent)\n                   (list (quote ok) (quote duplicate))\n                   (begin (set! sent (cons id sent)) (set! deliveries (+ deliveries 1)) (list (quote ok) (quote sent))))))\n           (define s1 (flow/start (ev-deliver-reminder 3) (list (quote m1) (quote a) (quote hi))))\n           (flow-run-host xport 5)\n           (define s2 (flow/start (ev-deliver-reminder 3) (list (quote m1) (quote a) (quote hi))))\n           (flow-run-host xport 5)\n           (list deliveries (flow/result (car (cdr s2))))")
        (list 1 (list "delivered" "m1" 1)))
      (ev-nt-check!
        "digest delivers every message in the batch"
        (ev/notify-run
          "(define s (flow/start (ev-deliver-digest 3) (list (list (quote a) (quote u1) (quote hi)) (list (quote b) (quote u2) (quote yo)))))\n           (flow-run-host (lambda (k p) (list (quote ok) (quote sent))) 10)\n           (flow/result (car (cdr s)))")
        (list
          (list "delivered" "a" 1)
          (list "delivered" "b" 1)))
      (ev-nt-check!
        "digest reports per-message outcomes independently"
        (ev/notify-run
          "(define (xport k p)\n             (let ((id (ev-msg-id p)))\n               (if (equal? id (quote b)) (list (quote retry) (quote flaky)) (list (quote ok) (quote sent)))))\n           (define s (flow/start (ev-deliver-digest 2) (list (list (quote a) (quote u1) (quote hi)) (list (quote b) (quote u2) (quote yo)) (list (quote c) (quote u3) (quote ya)))))\n           (flow-run-host xport 12)\n           (flow/result (car (cdr s)))")
        (list
          (list "delivered" "a" 1)
          (list "failed" "b" "flaky")
          (list "delivered" "c" 1)))
      (ev-nt-check!
        "delivery suspends until the transport responds"
        (ev/notify-run
          "(define s (flow/start (ev-deliver-reminder 3) (list (quote m1) (quote a) (quote hi))))\n           (flow/status (car (cdr s)))")
        "suspended"))))

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