;; lib/flow/tests/recovery.sx — Phase 3: crash recovery (store export/import + restart).
;;
;; "restart" is simulated within one program: (set! flow-store (list)) wipes the
;; in-memory store (process death), while flow-registry persists as it would after
;; reloading flow definitions. Recovery = import the exported (plain-data) store and
;; resume; the flow proc is re-resolved by name.

(define flow-rec-pass 0)
(define flow-rec-fail 0)
(define flow-rec-fails (list))

(define
  flow-rec-test
  (fn
    (name actual expected)
    (if
      (= actual expected)
      (set! flow-rec-pass (+ flow-rec-pass 1))
      (begin
        (set! flow-rec-fail (+ flow-rec-fail 1))
        (append! flow-rec-fails {:name name :expected expected :actual actual})))))

(define flow-r (fn (src) (flow-run src)))

;; ── export / wipe / import ──────────────────────────────────────
(flow-rec-test
  "export nulls the live procedure"
  (flow-r
    "(defflow w (lambda (x) (suspend (quote await)))) (flow/start w 10) (car (cdr (car (cdr (car (flow-store-export))))))")
  false)
(flow-rec-test
  "a wiped store loses the flow (process death)"
  (flow-r
    "(defflow w (lambda (x) (suspend (quote await)))) (define id (car (cdr (flow/start w 10)))) (set! flow-store (list)) (flow/resume id 1)")
  (list "flow-error" "no-such-flow"))
(flow-rec-test
  "import restores a wiped store and resume completes"
  (flow-r
    "(defflow w (sequence (lambda (x) (suspend (quote await))) (lambda (c) (list (quote done) c)))) (define id (car (cdr (flow/start w 10)))) (define saved (flow-store-export)) (set! flow-store (list)) (flow-store-import! saved) (flow/resume id 777)")
  (list "done" 777))

;; ── resumable scan ──────────────────────────────────────────────
(flow-rec-test
  "resumable-ids lists the suspended flow after import"
  (flow-r
    "(defflow w (lambda (x) (suspend (quote await)))) (define id (car (cdr (flow/start w 10)))) (define saved (flow-store-export)) (set! flow-store (list)) (flow-store-import! saved) (flow-resumable-ids)")
  (list 1))
(flow-rec-test
  "resumable-ids excludes completed flows"
  (flow-r
    "(defflow w (sequence (lambda (x) (suspend (quote await))) (lambda (c) c))) (define id (car (cdr (flow/start w 10)))) (flow/resume id 5) (flow-resumable-ids)")
  (list))
(flow-rec-test
  "resumable-ids excludes cancelled flows after import"
  (flow-r
    "(defflow w (lambda (x) (suspend (quote await)))) (define id (car (cdr (flow/start w 10)))) (flow/cancel id) (define saved (flow-store-export)) (set! flow-store (list)) (flow-store-import! saved) (flow-resumable-ids)")
  (list))

;; ── restart at every step ───────────────────────────────────────
(flow-rec-test
  "two suspends survive a restart between each step"
  (flow-r
    "(defflow two (sequence (lambda (x) (suspend (quote a))) (lambda (x) (suspend (quote b))) (lambda (x) (list (quote end) x)))) (define id (car (cdr (flow/start two 0)))) (define s1 (flow-store-export)) (set! flow-store (list)) (flow-store-import! s1) (flow/resume id 100) (define s2 (flow-store-export)) (set! flow-store (list)) (flow-store-import! s2) (flow/resume id 200)")
  (list "end" 200))
(flow-rec-test
  "import preserves the replay log (earlier value survives restart)"
  (flow-r
    "(defflow two (sequence (lambda (x) (suspend (quote a))) (lambda (x) (suspend (quote b))) (lambda (x) (list x)))) (define id (car (cdr (flow/start two 0)))) (flow/resume id 11) (define saved (flow-store-export)) (set! flow-store (list)) (flow-store-import! saved) (flow/resume id 22)")
  (list 22))

(define flow-rec-tests-run! (fn () {:total (+ flow-rec-pass flow-rec-fail) :passed flow-rec-pass :failed flow-rec-fail :fails flow-rec-fails}))
