;; identity/tests/facade.sx — the unified facade: one coordinator wiring ;; sessions+tokens, the audit ledger, and membership. Exercises the ;; cross-module integration (login/logout auditing, audit history, member ;; enrollment + projection) through the single `identity` door. (define id-facade-test-count 0) (define id-facade-test-pass 0) (define id-facade-test-fails (list)) (define id-facade-test (fn (name actual expected) (set! id-facade-test-count (+ id-facade-test-count 1)) (if (= actual expected) (set! id-facade-test-pass (+ id-facade-test-pass 1)) (append! id-facade-test-fails {:name name :expected expected :actual actual})))) (define idfc-ev erlang-eval-ast) (define idfcnm (fn (v) (get v :name))) (identity-load-all!) ;; ── login + logout are audited through the ledger ──────────────── (id-facade-test "login then logout records login, issue, logout in order" (idfcnm (idfc-ev "Svc = identity:start(),\n {ok, Sid, _Tok} = identity:login(Svc, alice, web, read),\n identity:logout(Svc, Sid),\n case identity:history(Svc, alice) of\n [login, issue, logout] -> ordered;\n Other -> Other\n end")) "ordered") (id-facade-test "revoking a token is audited" (idfcnm (idfc-ev "Svc = identity:start(),\n {ok, _Sid, Tok} = identity:login(Svc, alice, web, read),\n identity:revoke(Svc, Tok),\n case identity:history(Svc, alice) of\n [login, issue, revoke] -> ordered;\n Other -> Other\n end")) "ordered") (id-facade-test "history is per-subject" (idfc-ev "Svc = identity:start(),\n identity:login(Svc, alice, web, read),\n identity:login(Svc, bob, cli, read),\n identity:login(Svc, alice, mobile, read),\n length(identity:history(Svc, alice))") 4) ;; ── membership through the facade ──────────────────────────────── (id-facade-test "enroll makes the subject an active member" (idfcnm (idfc-ev "Svc = identity:start(),\n identity:enroll(Svc, alice, supporter),\n case identity:member_status(Svc, alice) of\n {ok, St, _} -> St;\n {none} -> none\n end")) "active") (id-facade-test "enroll keeps the tier" (idfcnm (idfc-ev "Svc = identity:start(),\n identity:enroll(Svc, alice, supporter),\n case identity:member_status(Svc, alice) of\n {ok, _, Tier} -> Tier\n end")) "supporter") (id-facade-test "an enrolled member projects per-app" (idfcnm (idfc-ev "Svc = identity:start(),\n identity:enroll(Svc, alice, basic),\n case identity:member_project(Svc, alice, market) of\n {member, _, App} -> App;\n {Tag, _} -> Tag\n end")) "market") (id-facade-test "a non-member projects as non_member" (idfcnm (idfc-ev "Svc = identity:start(),\n case identity:member_project(Svc, stranger, blog) of\n {member, _, _} -> member;\n {Tag, _} -> Tag\n end")) "non_member") ;; ── the facade still proves identity ───────────────────────────── (id-facade-test "verify still returns the subject after login" (idfcnm (idfc-ev "Svc = identity:start(),\n {ok, _Sid, Tok} = identity:login(Svc, alice, web, read),\n case identity:verify(Svc, Tok) of\n {active, Subject, _, _} -> Subject;\n {inactive} -> inactive\n end")) "alice") ;; ── identity and membership are distinct axes ──────────────────── (id-facade-test "logging in does not enroll membership" (idfcnm (idfc-ev "Svc = identity:start(),\n identity:login(Svc, alice, web, read),\n case identity:member_status(Svc, alice) of\n {ok, St, _} -> St;\n {none} -> none\n end")) "none") (define id-facade-test-summary (str "facade " id-facade-test-pass "/" id-facade-test-count))