identity: session-as-process — create/lookup/expire/revoke + idle timeout (11 tests)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 48s
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>
This commit is contained in:
118
lib/identity/tests/session.sx
Normal file
118
lib/identity/tests/session.sx
Normal file
@@ -0,0 +1,118 @@
|
||||
;; 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))
|
||||
Reference in New Issue
Block a user