;; identity/tests/clients.sx — OAuth client registry: registration,
;; public vs confidential authentication, and redirect_uri allow-listing.

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

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

(define idc-ev erlang-eval-ast)
(define idcnm (fn (v) (get v :name)))

(identity-load-clients!)

;; ── registration + lookup ────────────────────────────────────────

(id-clients-test
  "a registered client looks up its type"
  (idcnm
    (idc-ev
      "C = identity_clients:start(),\n       identity_clients:register(C, app1, confidential, s3cret, [uri1]),\n       case identity_clients:lookup(C, app1) of\n         {ok, Type, _} -> Type;\n         {error, W} -> W\n       end"))
  "confidential")

(id-clients-test
  "registering the same client twice is an error"
  (idcnm
    (idc-ev
      "C = identity_clients:start(),\n       identity_clients:register(C, app1, confidential, s3cret, [uri1]),\n       case identity_clients:register(C, app1, public, none, [uri1]) of\n         ok -> ok;\n         {error, W} -> W\n       end"))
  "exists")

(id-clients-test
  "looking up an unregistered client is unknown_client"
  (idcnm
    (idc-ev
      "C = identity_clients:start(),\n       case identity_clients:lookup(C, ghost) of\n         {ok, _, _} -> found;\n         {error, W} -> W\n       end"))
  "unknown_client")

;; ── confidential client authentication ───────────────────────────

(id-clients-test
  "a confidential client authenticates with the right secret"
  (idcnm
    (idc-ev
      "C = identity_clients:start(),\n       identity_clients:register(C, app1, confidential, s3cret, [uri1]),\n       case identity_clients:authenticate(C, app1, s3cret) of\n         {ok, Kind} -> Kind;\n         {error, W} -> W\n       end"))
  "confidential")

(id-clients-test
  "a confidential client with the wrong secret is invalid_client"
  (idcnm
    (idc-ev
      "C = identity_clients:start(),\n       identity_clients:register(C, app1, confidential, s3cret, [uri1]),\n       case identity_clients:authenticate(C, app1, wrongsecret) of\n         {ok, _} -> accepted;\n         {error, W} -> W\n       end"))
  "invalid_client")

(id-clients-test
  "a public client needs no secret to authenticate"
  (idcnm
    (idc-ev
      "C = identity_clients:start(),\n       identity_clients:register(C, spa, public, none, [uri1]),\n       case identity_clients:authenticate(C, spa, anything) of\n         {ok, Kind} -> Kind;\n         {error, W} -> W\n       end"))
  "public")

(id-clients-test
  "authenticating an unknown client is unknown_client"
  (idcnm
    (idc-ev
      "C = identity_clients:start(),\n       case identity_clients:authenticate(C, ghost, x) of\n         {ok, _} -> accepted;\n         {error, W} -> W\n       end"))
  "unknown_client")

;; ── redirect_uri allow-listing ───────────────────────────────────

(id-clients-test
  "a registered redirect_uri is valid"
  (idcnm
    (idc-ev
      "C = identity_clients:start(),\n       identity_clients:register(C, app1, confidential, s3cret, [uri1, uri2]),\n       case identity_clients:valid_redirect(C, app1, uri1) of\n         true -> yes;\n         false -> no\n       end"))
  "yes")

(id-clients-test
  "a second registered redirect_uri is also valid"
  (idcnm
    (idc-ev
      "C = identity_clients:start(),\n       identity_clients:register(C, app1, confidential, s3cret, [uri1, uri2]),\n       case identity_clients:valid_redirect(C, app1, uri2) of\n         true -> yes;\n         false -> no\n       end"))
  "yes")

(id-clients-test
  "an unregistered redirect_uri is rejected"
  (idcnm
    (idc-ev
      "C = identity_clients:start(),\n       identity_clients:register(C, app1, confidential, s3cret, [uri1]),\n       case identity_clients:valid_redirect(C, app1, evil_uri) of\n         true -> yes;\n         false -> no\n       end"))
  "no")

(id-clients-test
  "redirect validation for an unknown client is rejected"
  (idcnm
    (idc-ev
      "C = identity_clients:start(),\n       case identity_clients:valid_redirect(C, ghost, uri1) of\n         true -> yes;\n         false -> no\n       end"))
  "no")

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