;; lib/mod/tests/fed.sx — Phase 4: federation (mock fed-sx).

(define mod-fed-count 0)
(define mod-fed-pass 0)
(define mod-fed-fail 0)
(define mod-fed-failures (list))

(define
  mod-fed-test!
  (fn
    (name got expected)
    (begin
      (set! mod-fed-count (+ mod-fed-count 1))
      (if
        (= got expected)
        (set! mod-fed-pass (+ mod-fed-pass 1))
        (begin
          (set! mod-fed-fail (+ mod-fed-fail 1))
          (append!
            mod-fed-failures
            (str name "\n    expected: " expected "\n    got:      " got)))))))

(mod/reset!)
(mod/fed-reset!)

;; ── trust model (advisory by default) ──

(mod-fed-test! "trust initially false" (mod/trusted? "peerA" :mod) false)
(mod/grant-trust "peerA" :mod)
(mod-fed-test! "trust after grant" (mod/trusted? "peerA" :mod) true)
(mod-fed-test! "trust wrong scope" (mod/trusted? "peerA" :other) false)
(mod-fed-test! "trust other peer" (mod/trusted? "peerB" :mod) false)
(mod/revoke-trust "peerA" :mod)
(mod-fed-test! "trust after revoke" (mod/trusted? "peerA" :mod) false)

;; ── cross-instance reports ──

(define
  mod-fed-fr
  (mod/fed-receive-report "peerB" "alice" "bob" "this is spam"))
(mod-fed-test! "fed report assigned id r1" (mod/report-id mod-fed-fr) "r1")
(mod-fed-test! "fed report origin is peer" (mod/report-origin "r1") "peerB")
(define mod-fed-local (mod/report "carol" "dave" "fine post"))
(mod-fed-test!
  "local report origin is local"
  (mod/report-origin (mod/report-id mod-fed-local))
  "local")
(mod-fed-test!
  "engine decides fed report (spam → hide)"
  (get
    (mod/decide-report mod-fed-fr (list mod-fed-fr) mod/default-rules)
    :action)
  "hide")

;; ── decision sharing (outbox) ──

(define mod-fed-dec {:action "hide" :rule "spam-hide" :report-id "r1"})
(define
  mod-fed-shared
  (mod/fed-share-decision mod-fed-dec (list "peerB" "peerC")))
(mod-fed-test! "share returns notified peers" (len mod-fed-shared) 2)
(mod-fed-test! "outbox has two messages" (len (mod/fed-outbox)) 2)
(mod-fed-test!
  "outbox message type decision"
  (get (first (mod/fed-outbox)) :type)
  "decision")
(mod-fed-test!
  "outbox message addressed to peer"
  (get (first (mod/fed-outbox)) :to)
  "peerB")

;; ── receiving a peer decision: advisory unless trusted ──

(define mod-fed-untrusted (mod/fed-receive-decision "peerZ" {:action "remove" :rule "reviewer-remove" :report-id "rx"}))
(mod-fed-test!
  "untrusted decision not applied"
  (get mod-fed-untrusted :applied)
  false)
(mod-fed-test!
  "untrusted decision advisory"
  (get mod-fed-untrusted :advisory)
  true)
(mod-fed-test!
  "untrusted decision absent from applied log"
  (mod/fed-applied-action "rx")
  nil)
(mod-fed-test!
  "advisory log records suggestion"
  (len mod/*fed-advisory*)
  1)

(mod/grant-trust "peerT" :mod)
(define mod-fed-trusted (mod/fed-receive-decision "peerT" {:action "hide" :rule "spam-hide" :report-id "ry"}))
(mod-fed-test! "trusted decision applied" (get mod-fed-trusted :applied) true)
(mod-fed-test!
  "trusted decision binds locally"
  (get (mod/fed-applied-action "ry") :action)
  "hide")

;; ── revocation ──

(mod-fed-test!
  "applied action not yet revoked"
  (get (mod/fed-applied-action "ry") :revoked)
  false)
(mod/fed-revoke! "ry" "manual")
(mod-fed-test!
  "revoke marks applied action revoked"
  (get (mod/fed-applied-action "ry") :revoked)
  true)
(mod-fed-test!
  "revoke emits a revocation message"
  (mod/any? (fn (m) (= (get m :type) "revocation")) (mod/fed-outbox))
  true)

;; revoke-if-invalidated: proof still holds → no revocation
(define mod-fed-spam-r (mod/mk-report "rs" "a" "b" "this is spam"))
(define
  mod-fed-spam-d
  (mod/decide-report mod-fed-spam-r (list mod-fed-spam-r) mod/default-rules))
(mod-fed-test! "spam decision is hide" (get mod-fed-spam-d :action) "hide")
(define
  mod-fed-rev-same
  (mod/fed-revoke-if-invalidated
    mod-fed-spam-r
    mod-fed-spam-d
    (list mod-fed-spam-r)
    mod/default-rules))
(mod-fed-test!
  "valid proof → not revoked"
  (get mod-fed-rev-same :revoked)
  false)

;; exoneration invalidates the proof → revocation
(define
  mod-fed-exon-r
  (mod/attach-evidence mod-fed-spam-r (mod/mk-evidence "exonerated" "mod")))
(define
  mod-fed-rev-inv
  (mod/fed-revoke-if-invalidated
    mod-fed-exon-r
    mod-fed-spam-d
    (list mod-fed-exon-r)
    mod/default-rules))
(mod-fed-test!
  "invalidated proof → revoked"
  (get mod-fed-rev-inv :revoked)
  true)
(mod-fed-test!
  "re-decision after exoneration is keep"
  (get (get mod-fed-rev-inv :decision) :action)
  "keep")

(define mod-fed-tests-run! (fn () {:failures mod-fed-failures :total mod-fed-count :passed mod-fed-pass :failed mod-fed-fail}))
