Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 48s
Session is an Erlang process holding {subject, client, status}. lookup/
touch/expire/revoke are messages; expiry is the process's own
`receive ... after Ttl` timeout (RFC-agnostic; no global sweep), which
notifies the owner and tombstones. Tombstoned sessions answer lookups
with an explicit {error, expired|revoked}, never a silent dead mailbox.
Adds the conformance harness + scoreboard.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
119 lines
4.7 KiB
Plaintext
119 lines
4.7 KiB
Plaintext
;; 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))
|