;; 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))