;; identity/tests/session.sx — session-as-process: create, lookup,
;; touch, explicit expire, revoke, and idle-timeout self-expiry.
;; Negative paths are tested as first-class: a tombstoned session
;; answers {error, Status}, it does not go silent.

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

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

(define id-ev erlang-eval-ast)
(define idnm (fn (v) (get v :name)))

(identity-load-session!)

;; ── create + lookup ──────────────────────────────────────────────

(id-session-test
  "lookup of live session is active"
  (idnm
    (id-ev
      "Me = self(),\n       S = identity_session:start(s1, alice, web, Me, infinity),\n       case identity_session:lookup(S) of {ok, {_,_,_,St}} -> St end"))
  "active")

(id-session-test
  "lookup preserves subject"
  (idnm
    (id-ev
      "Me = self(),\n       S = identity_session:start(s1, alice, web, Me, infinity),\n       case identity_session:lookup(S) of {ok, {_,Subject,_,_}} -> Subject end"))
  "alice")

(id-session-test
  "lookup preserves client"
  (idnm
    (id-ev
      "Me = self(),\n       S = identity_session:start(s1, alice, web, Me, infinity),\n       case identity_session:lookup(S) of {ok, {_,_,Client,_}} -> Client end"))
  "web")

;; ── touch keeps a live session ───────────────────────────────────

(id-session-test
  "touch on live session is ok"
  (idnm
    (id-ev
      "Me = self(),\n       S = identity_session:start(s1, alice, web, Me, infinity),\n       identity_session:touch(S)"))
  "ok")

;; ── explicit expire ──────────────────────────────────────────────

(id-session-test
  "expire then lookup is error expired"
  (idnm
    (id-ev
      "Me = self(),\n       S = identity_session:start(s1, alice, web, Me, infinity),\n       identity_session:expire(S),\n       case identity_session:lookup(S) of {error, St} -> St end"))
  "expired")

(id-session-test
  "touch on expired session is error"
  (idnm
    (id-ev
      "Me = self(),\n       S = identity_session:start(s1, alice, web, Me, infinity),\n       identity_session:expire(S),\n       case identity_session:touch(S) of {error, St} -> St end"))
  "expired")

;; ── revoke is immediate ──────────────────────────────────────────

(id-session-test
  "revoke then lookup is error revoked"
  (idnm
    (id-ev
      "Me = self(),\n       S = identity_session:start(s1, alice, web, Me, infinity),\n       identity_session:revoke(S),\n       case identity_session:lookup(S) of {error, St} -> St end"))
  "revoked")

;; ── idle-timeout self-expiry ─────────────────────────────────────

(id-session-test
  "idle timeout notifies owner"
  (idnm
    (id-ev
      "Me = self(),\n       S = identity_session:start(s1, alice, web, Me, 50),\n       _ = identity_session:lookup(S),\n       receive {session_expired, Sid} -> Sid end"))
  "s1")

(id-session-test
  "lookup after idle timeout is error expired"
  (idnm
    (id-ev
      "Me = self(),\n       S = identity_session:start(s1, alice, web, Me, 50),\n       _ = identity_session:lookup(S),\n       receive {session_expired, _} -> ok end,\n       case identity_session:lookup(S) of {error, St} -> St end"))
  "expired")

;; ── isolation: sessions are independent processes ────────────────

(id-session-test
  "expiring one session leaves the other active"
  (idnm
    (id-ev
      "Me = self(),\n       A = identity_session:start(s1, alice, web, Me, infinity),\n       B = identity_session:start(s2, bob, web, Me, infinity),\n       identity_session:expire(A),\n       case identity_session:lookup(B) of {ok, {_,_,_,St}} -> St end"))
  "active")

;; ── clean stop ───────────────────────────────────────────────────

(id-session-test
  "stop returns ok"
  (idnm
    (id-ev
      "Me = self(),\n       S = identity_session:start(s1, alice, web, Me, infinity),\n       identity_session:stop(S)"))
  "ok")

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