;; lib/acl/tests/harden.sx — adversarial / cross-phase hardening.
;;
;; Diamond hierarchies, conflict resolution where deny must win through every
;; path, chain inheritance, cycle termination, multi-peer delegation, fact
;; validation, and audit save/restore.
;;
;; PROVER-FREE BY DESIGN: this suite calls only acl-permit? (which runs in
;; compiled Datalog, safe at any depth) plus pure data ops — never acl-explain /
;; acl-prove-d. The SX-side proof reconstructor recurses, and once the kernel
;; JIT-compiles it (after the explain/fed suites warm the process) it loops on
;; chains deeper than ~3 (substrate JIT bug — see plan Blockers). Proof
;; reconstruction is covered by tests/explain.sx (and federated proofs by
;; tests/fed.sx), both of which stay under the warm-process depth threshold.

(define acl-hd-pass 0)
(define acl-hd-fail 0)
(define acl-hd-failures (list))

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

(define acl-hd-p? (fn (db s a r) (acl-permit? db s a r)))

(define
  acl-hd-run-all!
  (fn
    ()
    (do
      (let
        ((grant-deny (acl-build-db (list (acl-child-of (quote r) (quote p1)) (acl-child-of (quote r) (quote p2)) (acl-grant (quote u) (quote read) (quote p1)) (acl-deny (quote u) (quote read) (quote p2)))))
          (both-grant
            (acl-build-db
              (list
                (acl-child-of (quote r) (quote p1))
                (acl-child-of (quote r) (quote p2))
                (acl-grant (quote u) (quote read) (quote p1))
                (acl-grant (quote u) (quote read) (quote p2))))))
        (do
          (acl-hd-check!
            "diamond resource: grant+deny parents -> deny wins"
            (acl-hd-p? grant-deny (quote u) (quote read) (quote r))
            false)
          (acl-hd-check!
            "diamond resource: both grant -> permit"
            (acl-hd-p? both-grant (quote u) (quote read) (quote r))
            true)
          (acl-hd-check!
            "diamond resource: deny does not leak to other parent"
            (acl-hd-p? grant-deny (quote u) (quote read) (quote p1))
            true)))
      (let
        ((grant-deny (acl-build-db (list (acl-member-of (quote alice) (quote g1)) (acl-member-of (quote alice) (quote g2)) (acl-grant (quote g1) (quote read) (quote doc)) (acl-deny (quote g2) (quote read) (quote doc)))))
          (both-grant
            (acl-build-db
              (list
                (acl-member-of (quote alice) (quote g1))
                (acl-member-of (quote alice) (quote g2))
                (acl-grant (quote g1) (quote read) (quote doc))
                (acl-grant (quote g2) (quote read) (quote doc))))))
        (do
          (acl-hd-check!
            "diamond group: grant+deny groups -> deny wins"
            (acl-hd-p? grant-deny (quote alice) (quote read) (quote doc))
            false)
          (acl-hd-check!
            "diamond group: both grant -> permit"
            (acl-hd-p? both-grant (quote alice) (quote read) (quote doc))
            true)))
      (let
        ((chain (acl-build-db (list (acl-member-of (quote a0) (quote a1)) (acl-member-of (quote a1) (quote a2)) (acl-member-of (quote a2) (quote a3)) (acl-member-of (quote a3) (quote a4)) (acl-grant (quote a4) (quote read) (quote res)))))
          (chain-deny
            (acl-build-db
              (list
                (acl-member-of (quote a0) (quote a1))
                (acl-member-of (quote a1) (quote a2))
                (acl-member-of (quote a2) (quote a3))
                (acl-member-of (quote a3) (quote a4))
                (acl-grant (quote a4) (quote read) (quote res))
                (acl-deny (quote a0) (quote read) (quote res))))))
        (do
          (acl-hd-check!
            "chain: top-group grant reaches leaf member"
            (acl-hd-p? chain (quote a0) (quote read) (quote res))
            true)
          (acl-hd-check!
            "chain: intermediate also covered"
            (acl-hd-p? chain (quote a2) (quote read) (quote res))
            true)
          (acl-hd-check!
            "chain: leaf-member deny overrides top grant"
            (acl-hd-p? chain-deny (quote a0) (quote read) (quote res))
            false)
          (acl-hd-check!
            "chain: deny on leaf does not block sibling level"
            (acl-hd-p? chain-deny (quote a1) (quote read) (quote res))
            true)))
      (let
        ((self-member (acl-build-db (list (acl-member-of (quote a) (quote a)) (acl-grant (quote a) (quote read) (quote r)))))
          (self-child
            (acl-build-db
              (list
                (acl-child-of (quote r) (quote r))
                (acl-grant (quote u) (quote read) (quote r)))))
          (two-cycle
            (acl-build-db
              (list
                (acl-member-of (quote x) (quote y))
                (acl-member-of (quote y) (quote x))
                (acl-grant (quote y) (quote read) (quote r))))))
        (do
          (acl-hd-check!
            "self-membership cycle terminates and grants"
            (acl-hd-p? self-member (quote a) (quote read) (quote r))
            true)
          (acl-hd-check!
            "self-child cycle terminates and grants"
            (acl-hd-p? self-child (quote u) (quote read) (quote r))
            true)
          (acl-hd-check!
            "two-node membership cycle terminates"
            (acl-hd-p? two-cycle (quote x) (quote read) (quote r))
            true)))
      (let
        ((db (acl-build-db (list (acl-trust (quote alpha) (quote full)) (acl-level-covers (quote full) (quote read)) (acl-member-of (quote alice) (quote team)) (acl-delegate (quote alpha) (quote team) (quote read) (quote doc)) (acl-deny (quote alice) (quote read) (quote doc))))))
        (acl-hd-check!
          "federated group grant, local member deny -> deny wins"
          (acl-hd-p? db (quote alice) (quote read) (quote doc))
          false))
      (let
        ((db (acl-build-db (list (acl-trust (quote alpha) (quote full)) (acl-level-covers (quote full) (quote read)) (acl-delegate (quote alpha) (quote bob) (quote read) (quote doc)) (acl-delegate (quote beta) (quote bob) (quote read) (quote doc))))))
        (acl-hd-check!
          "two peers delegate, one trusted -> permit"
          (acl-hd-p? db (quote bob) (quote read) (quote doc))
          true))
      (let
        ((db (acl-build-db (list (acl-trust (quote alpha) (quote full)) (acl-trust (quote beta) (quote full)) (acl-level-covers (quote full) (quote read)) (acl-delegate (quote alpha) (quote bob) (quote read) (quote doc)) (acl-delegate (quote beta) (quote bob) (quote read) (quote doc))))))
        (acl-hd-check!
          "two peers both trusted -> permit"
          (acl-hd-p? db (quote bob) (quote read) (quote doc))
          true))
      (let
        ((empty (acl-build-db (list))))
        (acl-hd-check!
          "empty db: nothing permitted"
          (acl-hd-p? empty (quote u) (quote read) (quote r))
          false))
      (do
        (acl-hd-check!
          "validate: clean set has no bad facts"
          (len
            (acl-validate-facts
              (list
                (acl-grant (quote u) (quote read) (quote p))
                (acl-member-of (quote u) (quote g))
                (acl-delegate (quote pe) (quote u) (quote read) (quote p)))))
          0)
        (acl-hd-check!
          "validate: facts-valid? true on clean set"
          (acl-facts-valid?
            (list (acl-grant (quote u) (quote read) (quote p))))
          true)
        (acl-hd-check!
          "validate: surfaces wrong-arity and unknown predicate"
          (len
            (acl-validate-facts
              (list
                (acl-grant (quote u) (quote read) (quote p))
                (list (quote grant) (quote u))
                (list (quote bogus) (quote x) (quote y)))))
          2)
        (acl-hd-check!
          "validate: empty set is valid"
          (acl-facts-valid? (list))
          true))
      (let
        ((db (acl-build-db (list (acl-grant (quote u) (quote read) (quote p)) (acl-deny (quote u) (quote edit) (quote p))))))
        (do
          (acl-audit-clear!)
          (acl-audit-decide! db (quote u) (quote read) (quote p))
          (acl-audit-decide! db (quote u) (quote edit) (quote p))
          (let
            ((snap (acl-audit-snapshot)))
            (do
              (acl-audit-clear!)
              (acl-hd-check!
                "audit: cleared count is 0"
                (acl-audit-count)
                0)
              (acl-audit-restore! snap)
              (acl-hd-check!
                "audit: restored count"
                (acl-audit-count)
                2)
              (acl-hd-check!
                "audit: restored last act"
                (get (first (acl-audit-tail 1)) :act)
                (quote edit))
              (acl-audit-decide! db (quote u) (quote comment) (quote p))
              (acl-hd-check!
                "audit: seq continues after restore"
                (get (first (acl-audit-tail 1)) :seq)
                2)
              (acl-hd-check!
                "audit: snapshot is an immutable copy"
                (len (get snap :entries))
                2)
              (acl-audit-clear!))))))))

(define
  acl-harden-tests-run!
  (fn
    ()
    (do
      (set! acl-hd-pass 0)
      (set! acl-hd-fail 0)
      (set! acl-hd-failures (list))
      (acl-hd-run-all!)
      {:failures acl-hd-failures :total (+ acl-hd-pass acl-hd-fail) :passed acl-hd-pass :failed acl-hd-fail})))
