;; identity/tests/grants.sx — the client-credentials grant (RFC 6749
;; §4.4): a confidential client authenticates and gets a token acting on
;; its own behalf — no end-user, no refresh token (§4.4.3). Public clients
;; cannot use it.

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

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

(define idg-ev erlang-eval-ast)
(define idgnm (fn (v) (get v :name)))

(identity-load-oauth!)

;; ── confidential client-credentials happy path ───────────────────

(id-grants-test
  "a confidential client obtains a working token"
  (idgnm
    (idg-ev
      "O = identity_oauth:start(),\n       identity_oauth:register_client(O, svc, confidential, sk, [uri1]),\n       {ok, T} = identity_oauth:client_credentials(O, svc, sk, batch),\n       case identity_oauth:introspect(O, T) of\n         {active, _, _, _} -> active;\n         {inactive} -> inactive\n       end"))
  "active")

(id-grants-test
  "the client-credentials token's subject is the client itself"
  (idgnm
    (idg-ev
      "O = identity_oauth:start(),\n       identity_oauth:register_client(O, svc, confidential, sk, [uri1]),\n       {ok, T} = identity_oauth:client_credentials(O, svc, sk, batch),\n       case identity_oauth:introspect(O, T) of\n         {active, Subject, _, _} -> Subject\n       end"))
  "svc")

(id-grants-test
  "the client-credentials token carries the requested scope"
  (idgnm
    (idg-ev
      "O = identity_oauth:start(),\n       identity_oauth:register_client(O, svc, confidential, sk, [uri1]),\n       {ok, T} = identity_oauth:client_credentials(O, svc, sk, reports),\n       case identity_oauth:introspect(O, T) of\n         {active, _, _, Scope} -> Scope\n       end"))
  "reports")

(id-grants-test
  "client-credentials issues no refresh token (single value)"
  (idgnm
    (idg-ev
      "O = identity_oauth:start(),\n       identity_oauth:register_client(O, svc, confidential, sk, [uri1]),\n       case identity_oauth:client_credentials(O, svc, sk, batch) of\n         {ok, _, _} -> pair;\n         {ok, _} -> single;\n         {error, W} -> W\n       end"))
  "single")

;; ── authentication failures ──────────────────────────────────────

(id-grants-test
  "a wrong client secret is invalid_client"
  (idgnm
    (idg-ev
      "O = identity_oauth:start(),\n       identity_oauth:register_client(O, svc, confidential, sk, [uri1]),\n       case identity_oauth:client_credentials(O, svc, wrong, batch) of\n         {ok, _} -> issued;\n         {error, W} -> W\n       end"))
  "invalid_client")

(id-grants-test
  "a public client cannot use client-credentials"
  (idgnm
    (idg-ev
      "O = identity_oauth:start(),\n       identity_oauth:register_client(O, spa, public, none, [uri1]),\n       case identity_oauth:client_credentials(O, spa, none, batch) of\n         {ok, _} -> issued;\n         {error, W} -> W\n       end"))
  "unauthorized_client")

(id-grants-test
  "an unregistered client cannot use client-credentials"
  (idgnm
    (idg-ev
      "O = identity_oauth:start(),\n       case identity_oauth:client_credentials(O, ghost, x, batch) of\n         {ok, _} -> issued;\n         {error, W} -> W\n       end"))
  "invalid_client")

;; ── independence + real revocation for client tokens ─────────────

(id-grants-test
  "two confidential clients get independent tokens"
  (idgnm
    (idg-ev
      "O = identity_oauth:start(),\n       identity_oauth:register_client(O, svc1, confidential, k1, [uri1]),\n       identity_oauth:register_client(O, svc2, confidential, k2, [uri1]),\n       {ok, _T1} = identity_oauth:client_credentials(O, svc1, k1, batch),\n       {ok, T2} = identity_oauth:client_credentials(O, svc2, k2, batch),\n       case identity_oauth:introspect(O, T2) of\n         {active, Subject, _, _} -> Subject\n       end"))
  "svc2")

(id-grants-test
  "a client-credentials token can be revoked"
  (idgnm
    (idg-ev
      "O = identity_oauth:start(),\n       identity_oauth:register_client(O, svc, confidential, sk, [uri1]),\n       {ok, T} = identity_oauth:client_credentials(O, svc, sk, batch),\n       identity_oauth:revoke(O, T),\n       case identity_oauth:introspect(O, T) of\n         {active, _, _, _} -> still_valid;\n         {inactive} -> inactive\n       end"))
  "inactive")

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