;; identity/tests/account.sx — the account-security surface: \"apps with
;; access\" (grants_for / identity:grants) plus \"disconnect this app\"
;; (revoke_app / identity:revoke_app). Completes the per-subject view+action
;; pair alongside sessions and history.

(define id-acct-test-count 0)
(define id-acct-test-pass 0)
(define id-acct-test-fails (list))

(define
  id-acct-test
  (fn
    (name actual expected)
    (set! id-acct-test-count (+ id-acct-test-count 1))
    (if
      (= actual expected)
      (set! id-acct-test-pass (+ id-acct-test-pass 1))
      (append! id-acct-test-fails {:name name :expected expected :actual actual}))))

(define ida-ev erlang-eval-ast)
(define idanm (fn (v) (get v :name)))

(identity-load-all!)

;; ── token-level grants_for ───────────────────────────────────────

(id-acct-test
  "grants_for lists a subject's active grants"
  (ida-ev
    "R = identity_tokens:start(),\n     identity_tokens:issue(R, alice, web, read),\n     identity_tokens:issue(R, alice, cli, write),\n     identity_tokens:issue(R, bob, web, read),\n     length(identity_tokens:grants_for(R, alice))")
  2)

(id-acct-test
  "grants_for excludes revoked grants"
  (ida-ev
    "R = identity_tokens:start(),\n     {ok, A} = identity_tokens:issue(R, alice, web, read),\n     identity_tokens:issue(R, alice, cli, write),\n     identity_tokens:revoke(R, A),\n     length(identity_tokens:grants_for(R, alice))")
  1)

(id-acct-test
  "grants_for is empty for a subject with none"
  (ida-ev
    "R = identity_tokens:start(),\n     identity_tokens:issue(R, alice, web, read),\n     length(identity_tokens:grants_for(R, ghost))")
  0)

(id-acct-test
  "each grant entry carries the client"
  (idanm
    (ida-ev
      "R = identity_tokens:start(),\n       identity_tokens:issue(R, alice, web, read),\n       case identity_tokens:grants_for(R, alice) of\n         [{Client, _Scope}] -> Client;\n         _ -> other\n       end"))
  "web")

;; ── token-level revoke_app (\"disconnect this app\") ────────────────

(id-acct-test
  "revoke_app revokes all of a subject's grants for one client"
  (ida-ev
    "R = identity_tokens:start(),\n     identity_tokens:issue(R, alice, web, read),\n     identity_tokens:issue(R, alice, web, write),\n     identity_tokens:issue(R, alice, cli, read),\n     identity_tokens:revoke_app(R, alice, web),\n     length(identity_tokens:grants_for(R, alice))")
  1)

(id-acct-test
  "revoke_app deactivates that client's tokens"
  (idanm
    (ida-ev
      "R = identity_tokens:start(),\n       {ok, T} = identity_tokens:issue(R, alice, web, read),\n       identity_tokens:revoke_app(R, alice, web),\n       case identity_tokens:introspect(R, T) of\n         {active, _, _, _} -> active;\n         {inactive} -> inactive\n       end"))
  "inactive")

(id-acct-test
  "revoke_app leaves another subject's same-client grant intact"
  (ida-ev
    "R = identity_tokens:start(),\n     identity_tokens:issue(R, alice, web, read),\n     identity_tokens:issue(R, bob, web, read),\n     identity_tokens:revoke_app(R, alice, web),\n     length(identity_tokens:grants_for(R, bob))")
  1)

;; ── facade-level grants + revoke_app ─────────────────────────────

(id-acct-test
  "identity:grants lists apps a subject has logged into"
  (ida-ev
    "Svc = identity:start(),\n     identity:login(Svc, alice, web, read),\n     identity:login(Svc, alice, mobile, read),\n     length(identity:grants(Svc, alice))")
  2)

(id-acct-test
  "identity:revoke_app disconnects one app, leaving the rest"
  (ida-ev
    "Svc = identity:start(),\n     identity:login(Svc, alice, web, read),\n     identity:login(Svc, alice, mobile, read),\n     identity:revoke_app(Svc, alice, web),\n     length(identity:grants(Svc, alice))")
  1)

(id-acct-test
  "identity:grants is per-subject"
  (ida-ev
    "Svc = identity:start(),\n     identity:login(Svc, alice, web, read),\n     identity:login(Svc, bob, web, read),\n     length(identity:grants(Svc, bob))")
  1)

(id-acct-test
  "revoke_app is audited as a revoke"
  (idanm
    (ida-ev
      "Svc = identity:start(),\n       identity:login(Svc, alice, web, read),\n       identity:revoke_app(Svc, alice, web),\n       case identity:history(Svc, alice) of\n         [login, issue, revoke] -> audited;\n         Other -> Other\n       end"))
  "audited")

(define
  id-acct-test-summary
  (str "account " id-acct-test-pass "/" id-acct-test-count))
