Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 24s
http_server:route/3(Req, Cfg, Kernel) is the new extended entry
point: folds the kernel reference (typically the registered
nx_kernel atom) into Cfg as {kernel, Kernel}. route/2 is
unchanged and stays the M1 surface.
The dispatch chain gained Cfg threading all the way down:
dispatch/3 -> dispatch/4 (M, P, F, Cfg)
actor_get/2 -> actor_get/3 (Rest, F, Cfg)
actor_subresource_get/3 -> /4 (Id, Sub, F, Cfg)
actor_outbox_response_for/3 (new) reads :kernel from Cfg and,
when the kernel atom is registered AND the actor exists, renders
'tip: <N>' alongside the actor id in text / JSON / SX content-
negotiated bodies. Unknown actors or unregistered kernels fall
back to the 4a stub.
Inbox / followers / following handlers accept Cfg but ignore it
for now — they layer real state lookup in 4d/4e/Step 5+.
Substrate gotcha logged in the Progress log: try/of/catch around
gen_server:call(nx_kernel, _) deadlocks in this port's scheduler
(probably the catch frame's mask defers reply delivery). The
live kernel_log_tip/2 helper does a bare call + integer guard
instead. nx_kernel_multi.sh already proves bare gen_server:call
into the same kernel works correctly.
8 new cases in next/tests/http_multi_actor.sh (33/33 total):
- route/3 with registered kernel: outbox body includes tip=0
- tip advances after POST publish through route/3 + token map
- unknown actor (ghost) falls back to 4a stub (no tip:)
- unregistered kernel ref falls back to stub
- JSON Accept renders {"outbox":"alice","tip":0}
- SX Accept renders (outbox "alice" :tip 0)
- Bob's outbox tip stays 0 while Alice publishes (per-actor)
- route/2 path unchanged: no tip field in body
Conformance 761/761. 121/121 across 10 Step-4-adjacent suites
(http_multi_actor, http_route, http_publish, http_post_format,
http_marshal, http_publish_fold, http_listen_bif, http_server_start,
nx_kernel_multi, bootstrap_start, actor_lifecycle).
255 lines
24 KiB
Bash
Executable File
255 lines
24 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# next/tests/http_multi_actor.sh — m2 Step 4 tests (4a: per-actor
|
|
# URL sub-paths).
|
|
#
|
|
# Per design §16.1 each actor has:
|
|
# GET /actors/<id> actor doc (M1)
|
|
# GET /actors/<id>/outbox outbox listing (4a: stub)
|
|
# GET /actors/<id>/inbox inbox listing (4a: stub)
|
|
# GET /actors/<id>/followers follower list (4a: stub)
|
|
# GET /actors/<id>/following following list (4a: stub)
|
|
# POST /actors/<id>/inbox peer delivery (4a: 202 stub; Step 5 real)
|
|
#
|
|
# 4b-4e wire the routes to per-actor kernel state + token map.
|
|
|
|
set -uo pipefail
|
|
cd "$(git rev-parse --show-toplevel)"
|
|
|
|
SX_SERVER="${SX_SERVER:-hosts/ocaml/_build/default/bin/sx_server.exe}"
|
|
if [ ! -x "$SX_SERVER" ]; then
|
|
SX_SERVER="/root/rose-ash/hosts/ocaml/_build/default/bin/sx_server.exe"
|
|
fi
|
|
if [ ! -x "$SX_SERVER" ]; then
|
|
echo "ERROR: sx_server.exe not found." >&2
|
|
exit 1
|
|
fi
|
|
|
|
VERBOSE="${1:-}"
|
|
PASS=0; FAIL=0; ERRORS=""
|
|
TMPFILE=$(mktemp); trap "rm -f $TMPFILE" EXIT
|
|
|
|
cat > "$TMPFILE" <<'EPOCHS'
|
|
(epoch 1)
|
|
(load "lib/erlang/tokenizer.sx")
|
|
(load "lib/erlang/parser.sx")
|
|
(load "lib/erlang/parser-core.sx")
|
|
(load "lib/erlang/parser-expr.sx")
|
|
(load "lib/erlang/parser-module.sx")
|
|
(load "lib/erlang/transpile.sx")
|
|
(load "lib/erlang/runtime.sx")
|
|
(load "lib/erlang/vm/dispatcher.sx")
|
|
(epoch 2)
|
|
(eval "(get (erlang-load-module (file-read \"next/kernel/http_server.erl\")) :name)")
|
|
(epoch 3)
|
|
(eval "(er-load-gen-server!)")
|
|
(epoch 4)
|
|
(eval "(get (erlang-load-module (file-read \"next/kernel/envelope.erl\")) :name)")
|
|
(epoch 5)
|
|
(eval "(get (erlang-load-module (file-read \"next/kernel/log.erl\")) :name)")
|
|
(epoch 6)
|
|
(eval "(get (erlang-load-module (file-read \"next/kernel/pipeline.erl\")) :name)")
|
|
(epoch 7)
|
|
(eval "(get (erlang-load-module (file-read \"next/kernel/outbox.erl\")) :name)")
|
|
(epoch 8)
|
|
(eval "(get (erlang-load-module (file-read \"next/kernel/nx_kernel.erl\")) :name)")
|
|
|
|
;; split_first_slash sanity
|
|
(epoch 10)
|
|
(eval "(get (erlang-eval-ast \"http_server:split_first_slash(<<97,108,105,99,101>>) =:= <<97,108,105,99,101>>\") :name)")
|
|
(epoch 11)
|
|
(eval "(get (erlang-eval-ast \"http_server:split_first_slash(<<97,108,105,99,101,47,105,110,98,111,120>>) =:= {<<97,108,105,99,101>>, <<105,110,98,111,120>>}\") :name)")
|
|
(epoch 12)
|
|
(eval "(get (erlang-eval-ast \"http_server:split_first_slash(<<97,108,105,99,101,47>>) =:= {<<97,108,105,99,101>>, <<>>}\") :name)")
|
|
|
|
;; GET /actors/alice returns actor doc (regression check — M1 path)
|
|
(epoch 20)
|
|
(eval "(get (erlang-eval-ast \"Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101>>}, {headers, []}, {body, <<>>}], R = http_server:route(Req), case R of [{status, 200}, _, {body, B}] -> http_server:match_prefix(<<97,99,116,111,114,58>>, B) =/= nomatch; _ -> false end\") :name)")
|
|
|
|
;; GET /actors/alice/outbox returns outbox stub
|
|
(epoch 21)
|
|
(eval "(get (erlang-eval-ast \"Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,111,117,116,98,111,120>>}, {headers, []}, {body, <<>>}], R = http_server:route(Req), case R of [{status, 200}, _, {body, B}] -> http_server:match_prefix(<<111,117,116,98,111,120,58>>, B) =/= nomatch; _ -> false end\") :name)")
|
|
|
|
;; GET /actors/alice/inbox returns inbox stub
|
|
(epoch 22)
|
|
(eval "(get (erlang-eval-ast \"Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,105,110,98,111,120>>}, {headers, []}, {body, <<>>}], R = http_server:route(Req), case R of [{status, 200}, _, {body, B}] -> http_server:match_prefix(<<105,110,98,111,120,58>>, B) =/= nomatch; _ -> false end\") :name)")
|
|
|
|
;; GET /actors/alice/followers returns followers stub
|
|
(epoch 23)
|
|
(eval "(get (erlang-eval-ast \"Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,102,111,108,108,111,119,101,114,115>>}, {headers, []}, {body, <<>>}], R = http_server:route(Req), case R of [{status, 200}, _, {body, B}] -> http_server:match_prefix(<<102,111,108,108,111,119,101,114,115,58>>, B) =/= nomatch; _ -> false end\") :name)")
|
|
|
|
;; GET /actors/alice/following returns following stub
|
|
(epoch 24)
|
|
(eval "(get (erlang-eval-ast \"Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,102,111,108,108,111,119,105,110,103>>}, {headers, []}, {body, <<>>}], R = http_server:route(Req), case R of [{status, 200}, _, {body, B}] -> http_server:match_prefix(<<102,111,108,108,111,119,105,110,103,58>>, B) =/= nomatch; _ -> false end\") :name)")
|
|
|
|
;; POST /actors/alice/inbox returns 202 with "accepted"
|
|
(epoch 25)
|
|
(eval "(get (erlang-eval-ast \"Req = [{method, <<80,79,83,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,105,110,98,111,120>>}, {headers, []}, {body, <<>>}], R = http_server:route(Req), case R of [{status, 202}, _, {body, B}] -> http_server:match_prefix(<<97,99,99,101,112,116,101,100>>, B) =/= nomatch; _ -> false end\") :name)")
|
|
|
|
;; GET /actors/alice/unknown returns 404
|
|
(epoch 26)
|
|
(eval "(get (erlang-eval-ast \"Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,117,110,107,110,111,119,110>>}, {headers, []}, {body, <<>>}], R = http_server:route(Req), case R of [{status, 404}, _, _] -> true; _ -> false end\") :name)")
|
|
|
|
;; POST /actors/alice/unknown returns 404
|
|
(epoch 27)
|
|
(eval "(get (erlang-eval-ast \"Req = [{method, <<80,79,83,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,117,110,107,110,111,119,110>>}, {headers, []}, {body, <<>>}], R = http_server:route(Req), case R of [{status, 404}, _, _] -> true; _ -> false end\") :name)")
|
|
|
|
;; GET /actors/ (no id) returns 404 (existing behaviour preserved)
|
|
(epoch 28)
|
|
(eval "(get (erlang-eval-ast \"Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47>>}, {headers, []}, {body, <<>>}], R = http_server:route(Req), case R of [{status, 404}, _, _] -> true; _ -> false end\") :name)")
|
|
|
|
;; GET /actors/bob/outbox carries bob's id in the stub body
|
|
(epoch 29)
|
|
(eval "(get (erlang-eval-ast \"Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,98,111,98,47,111,117,116,98,111,120>>}, {headers, []}, {body, <<>>}], R = http_server:route(Req), [{status, 200}, _, {body, B}] = R, http_server:match_prefix(<<111,117,116,98,111,120,58,32,98,111,98>>, B) =/= nomatch\") :name)")
|
|
|
|
;; Accept: application/json on /actors/alice/outbox -> JSON stub
|
|
(epoch 30)
|
|
(eval "(get (erlang-eval-ast \"AcceptKey = <<97,99,99,101,112,116>>, AcceptVal = <<97,112,112,108,105,99,97,116,105,111,110,47,106,115,111,110>>, Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,111,117,116,98,111,120>>}, {headers, [{AcceptKey, AcceptVal}]}, {body, <<>>}], R = http_server:route(Req), [_, _, {body, B}] = R, http_server:match_prefix(<<123,34,111,117,116,98,111,120,34>>, B) =/= nomatch\") :name)")
|
|
|
|
;; Accept: application/sx on /actors/alice/inbox -> SX stub
|
|
(epoch 31)
|
|
(eval "(get (erlang-eval-ast \"AcceptKey = <<97,99,99,101,112,116>>, AcceptVal = <<97,112,112,108,105,99,97,116,105,111,110,47,115,120>>, Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,105,110,98,111,120>>}, {headers, [{AcceptKey, AcceptVal}]}, {body, <<>>}], R = http_server:route(Req), [_, _, {body, B}] = R, http_server:match_prefix(<<40,105,110,98,111,120,32>>, B) =/= nomatch\") :name)")
|
|
|
|
;; Accept: application/json on /actors/alice/followers -> JSON stub
|
|
(epoch 32)
|
|
(eval "(get (erlang-eval-ast \"AcceptKey = <<97,99,99,101,112,116>>, AcceptVal = <<97,112,112,108,105,99,97,116,105,111,110,47,106,115,111,110>>, Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,102,111,108,108,111,119,101,114,115>>}, {headers, [{AcceptKey, AcceptVal}]}, {body, <<>>}], R = http_server:route(Req), [_, _, {body, B}] = R, http_server:match_prefix(<<123,34,102,111,108,108,111,119,101,114,115,34>>, B) =/= nomatch\") :name)")
|
|
|
|
;; ── Step 4b: token -> ActorId map ──────────────────────────────
|
|
;; Each test inlines start_link + add_actor + Cfg with :tokens
|
|
;; proplist mapping per-actor bearer tokens. Tokens look like
|
|
;; "alice-token" = <<97,108,105,99,101,45,116,111,107,101,110>>
|
|
;; (bytes spelled) and "bob-token" = <<98,111,98,45,116,111,107,101,110>>.
|
|
|
|
(epoch 40)
|
|
(eval "(get (erlang-eval-ast \"AK = <<1,2,3,4>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], BK = <<5,6,7,8>>, BKS = [{key_id,k1},{algorithm,ed25519},{value,BK}], BAS = [{public_keys,[[{id,k1},{created,0},{value,BK}]]}], nx_kernel:start_link(alice, AKS, AAS), nx_kernel:add_actor(bob, BKS, BAS), AliceTok = <<97,108,105,99,101,45,116,111,107,101,110>>, BobTok = <<98,111,98,45,116,111,107,101,110>>, AuthKey = <<97,117,116,104,111,114,105,122,97,116,105,111,110>>, AliceAuth = <<66,101,97,114,101,114,32,97,108,105,99,101,45,116,111,107,101,110>>, Cfg = [{tokens, [{AliceTok, alice}, {BobTok, bob}]}], Req = [{method, <<80,79,83,84>>}, {path, http_server:activity_path()}, {headers, [{AuthKey, AliceAuth}]}, {body, <<104,105>>}], R = http_server:route(Req, Cfg), case R of [{status, 200}, _, {body, B}] -> http_server:match_prefix(<<99,105,100,58,32>>, B) =/= nomatch; _ -> false end\") :name)")
|
|
|
|
;; Alice token publishes to alice's bucket (log_tip alice = 1, bob = 0)
|
|
(epoch 41)
|
|
(eval "(get (erlang-eval-ast \"AK = <<1,2,3,4>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], BK = <<5,6,7,8>>, BKS = [{key_id,k1},{algorithm,ed25519},{value,BK}], BAS = [{public_keys,[[{id,k1},{created,0},{value,BK}]]}], nx_kernel:start_link(alice, AKS, AAS), nx_kernel:add_actor(bob, BKS, BAS), AliceTok = <<97,108,105,99,101,45,116,111,107,101,110>>, BobTok = <<98,111,98,45,116,111,107,101,110>>, AuthKey = <<97,117,116,104,111,114,105,122,97,116,105,111,110>>, AliceAuth = <<66,101,97,114,101,114,32,97,108,105,99,101,45,116,111,107,101,110>>, Cfg = [{tokens, [{AliceTok, alice}, {BobTok, bob}]}], Req = [{method, <<80,79,83,84>>}, {path, http_server:activity_path()}, {headers, [{AuthKey, AliceAuth}]}, {body, <<104,105>>}], http_server:route(Req, Cfg), {nx_kernel:log_tip_for(alice), nx_kernel:log_tip_for(bob)} =:= {1, 0}\") :name)")
|
|
|
|
;; Bob token publishes to bob's bucket
|
|
(epoch 42)
|
|
(eval "(get (erlang-eval-ast \"AK = <<1,2,3,4>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], BK = <<5,6,7,8>>, BKS = [{key_id,k1},{algorithm,ed25519},{value,BK}], BAS = [{public_keys,[[{id,k1},{created,0},{value,BK}]]}], nx_kernel:start_link(alice, AKS, AAS), nx_kernel:add_actor(bob, BKS, BAS), AliceTok = <<97,108,105,99,101,45,116,111,107,101,110>>, BobTok = <<98,111,98,45,116,111,107,101,110>>, AuthKey = <<97,117,116,104,111,114,105,122,97,116,105,111,110>>, BobAuth = <<66,101,97,114,101,114,32,98,111,98,45,116,111,107,101,110>>, Cfg = [{tokens, [{AliceTok, alice}, {BobTok, bob}]}], Req = [{method, <<80,79,83,84>>}, {path, http_server:activity_path()}, {headers, [{AuthKey, BobAuth}]}, {body, <<104,105>>}], http_server:route(Req, Cfg), {nx_kernel:log_tip_for(alice), nx_kernel:log_tip_for(bob)} =:= {0, 1}\") :name)")
|
|
|
|
;; Mixed token stream -> independent logs
|
|
(epoch 43)
|
|
(eval "(get (erlang-eval-ast \"AK = <<1,2,3,4>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], BK = <<5,6,7,8>>, BKS = [{key_id,k1},{algorithm,ed25519},{value,BK}], BAS = [{public_keys,[[{id,k1},{created,0},{value,BK}]]}], nx_kernel:start_link(alice, AKS, AAS), nx_kernel:add_actor(bob, BKS, BAS), AliceTok = <<97,108,105,99,101,45,116,111,107,101,110>>, BobTok = <<98,111,98,45,116,111,107,101,110>>, AuthKey = <<97,117,116,104,111,114,105,122,97,116,105,111,110>>, AliceAuth = <<66,101,97,114,101,114,32,97,108,105,99,101,45,116,111,107,101,110>>, BobAuth = <<66,101,97,114,101,114,32,98,111,98,45,116,111,107,101,110>>, Cfg = [{tokens, [{AliceTok, alice}, {BobTok, bob}]}], AliceReq = [{method, <<80,79,83,84>>}, {path, http_server:activity_path()}, {headers, [{AuthKey, AliceAuth}]}, {body, <<104,105>>}], BobReq = [{method, <<80,79,83,84>>}, {path, http_server:activity_path()}, {headers, [{AuthKey, BobAuth}]}, {body, <<104,105>>}], http_server:route(AliceReq, Cfg), http_server:route(BobReq, Cfg), http_server:route(AliceReq, Cfg), {nx_kernel:log_tip_for(alice), nx_kernel:log_tip_for(bob)} =:= {2, 1}\") :name)")
|
|
|
|
;; Token not in :tokens map and no :publish_token -> 401
|
|
(epoch 44)
|
|
(eval "(get (erlang-eval-ast \"AK = <<1,2,3,4>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], nx_kernel:start_link(alice, AKS, AAS), AliceTok = <<97,108,105,99,101,45,116,111,107,101,110>>, AuthKey = <<97,117,116,104,111,114,105,122,97,116,105,111,110>>, GhostAuth = <<66,101,97,114,101,114,32,103,104,111,115,116>>, Cfg = [{tokens, [{AliceTok, alice}]}], Req = [{method, <<80,79,83,84>>}, {path, http_server:activity_path()}, {headers, [{AuthKey, GhostAuth}]}, {body, <<104,105>>}], case http_server:route(Req, Cfg) of [{status, 401}, _, _] -> true; _ -> false end\") :name)")
|
|
|
|
;; Legacy :publish_token still works (M1 back-compat)
|
|
(epoch 45)
|
|
(eval "(get (erlang-eval-ast \"AK = <<1,2,3,4>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], nx_kernel:start_link(alice, AKS, AAS), Tok = <<102,111,111>>, AuthKey = <<97,117,116,104,111,114,105,122,97,116,105,111,110>>, AuthVal = <<66,101,97,114,101,114,32,102,111,111>>, Cfg = [{publish_token, Tok}], Req = [{method, <<80,79,83,84>>}, {path, http_server:activity_path()}, {headers, [{AuthKey, AuthVal}]}, {body, <<104,105>>}], R = http_server:route(Req, Cfg), case R of [{status, 200}, _, {body, B}] -> http_server:match_prefix(<<99,105,100,58,32>>, B) =/= nomatch; _ -> false end\") :name)")
|
|
|
|
;; :tokens takes precedence; legacy :publish_token still resolved on miss
|
|
(epoch 46)
|
|
(eval "(get (erlang-eval-ast \"AK = <<1,2,3,4>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], BK = <<5,6,7,8>>, BKS = [{key_id,k1},{algorithm,ed25519},{value,BK}], BAS = [{public_keys,[[{id,k1},{created,0},{value,BK}]]}], nx_kernel:start_link(alice, AKS, AAS), nx_kernel:add_actor(bob, BKS, BAS), AliceTok = <<97,108,105,99,101,45,116,111,107,101,110>>, LegacyTok = <<102,111,111>>, AuthKey = <<97,117,116,104,111,114,105,122,97,116,105,111,110>>, AliceAuth = <<66,101,97,114,101,114,32,97,108,105,99,101,45,116,111,107,101,110>>, LegacyAuth = <<66,101,97,114,101,114,32,102,111,111>>, Cfg = [{tokens, [{AliceTok, alice}]}, {publish_token, LegacyTok}], Req1 = [{method, <<80,79,83,84>>}, {path, http_server:activity_path()}, {headers, [{AuthKey, AliceAuth}]}, {body, <<104,105>>}], Req2 = [{method, <<80,79,83,84>>}, {path, http_server:activity_path()}, {headers, [{AuthKey, LegacyAuth}]}, {body, <<104,105>>}], http_server:route(Req1, Cfg), http_server:route(Req2, Cfg), {nx_kernel:log_tip_for(alice), nx_kernel:log_tip_for(bob)} =:= {2, 0}\") :name)")
|
|
|
|
;; ── Step 4c: route/3 with kernel access ───────────────────────
|
|
;; route/3 folds the Kernel into Cfg under :kernel. The outbox
|
|
;; sub-resource handler now reads :kernel and includes "tip: N"
|
|
;; when the actor exists in the kernel. Other handlers ignore the
|
|
;; field for now (they layer real state in 4d/4e).
|
|
|
|
;; route/3 with kernel reference: GET /actors/alice/outbox includes log tip
|
|
(epoch 50)
|
|
(eval "(get (erlang-eval-ast \"AK = <<1,2,3,4>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], nx_kernel:start_link(alice, AKS, AAS), Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,111,117,116,98,111,120>>}, {headers, []}, {body, <<>>}], R = http_server:route(Req, [], nx_kernel), [_, _, {body, B}] = R, http_server:match_prefix(<<111,117,116,98,111,120,58,32,97,108,105,99,101,10,116,105,112,58,32,48>>, B) =/= nomatch\") :name)")
|
|
|
|
;; route/3 with kernel reference: outbox tip advances after publish
|
|
(epoch 51)
|
|
(eval "(get (erlang-eval-ast \"AK = <<1,2,3,4>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], nx_kernel:start_link(alice, AKS, AAS), AliceTok = <<97,108,105,99,101,45,116,111,107,101,110>>, AuthKey = <<97,117,116,104,111,114,105,122,97,116,105,111,110>>, AliceAuth = <<66,101,97,114,101,114,32,97,108,105,99,101,45,116,111,107,101,110>>, Cfg = [{tokens, [{AliceTok, alice}]}], PostReq = [{method, <<80,79,83,84>>}, {path, http_server:activity_path()}, {headers, [{AuthKey, AliceAuth}]}, {body, <<104,105>>}], http_server:route(PostReq, Cfg, nx_kernel), GetReq = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,111,117,116,98,111,120>>}, {headers, []}, {body, <<>>}], R = http_server:route(GetReq, [], nx_kernel), [_, _, {body, B}] = R, http_server:match_prefix(<<111,117,116,98,111,120,58,32,97,108,105,99,101,10,116,105,112,58,32,49>>, B) =/= nomatch\") :name)")
|
|
|
|
;; route/3 with unknown actor -> falls back to /2 stub (no tip)
|
|
(epoch 52)
|
|
(eval "(get (erlang-eval-ast \"AK = <<1,2,3,4>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], nx_kernel:start_link(alice, AKS, AAS), Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,103,104,111,115,116,47,111,117,116,98,111,120>>}, {headers, []}, {body, <<>>}], R = http_server:route(Req, [], nx_kernel), [_, _, {body, B}] = R, http_server:match_prefix(<<111,117,116,98,111,120,58,32,103,104,111,115,116,10>>, B) =/= nomatch andalso http_server:match_prefix(<<116,105,112,58>>, B) =:= nomatch\") :name)")
|
|
|
|
;; route/3 without kernel registered -> falls back to stub
|
|
(epoch 53)
|
|
(eval "(get (erlang-eval-ast \"Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,111,117,116,98,111,120>>}, {headers, []}, {body, <<>>}], R = http_server:route(Req, [], unregistered_kernel), [_, _, {body, B}] = R, http_server:match_prefix(<<111,117,116,98,111,120,58,32,97,108,105,99,101,10>>, B) =/= nomatch andalso http_server:match_prefix(<<116,105,112,58>>, B) =:= nomatch\") :name)")
|
|
|
|
;; route/3 with kernel + JSON Accept -> JSON body carries :tip
|
|
(epoch 54)
|
|
(eval "(get (erlang-eval-ast \"AK = <<1,2,3,4>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], nx_kernel:start_link(alice, AKS, AAS), AcceptKey = <<97,99,99,101,112,116>>, AcceptVal = <<97,112,112,108,105,99,97,116,105,111,110,47,106,115,111,110>>, Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,111,117,116,98,111,120>>}, {headers, [{AcceptKey, AcceptVal}]}, {body, <<>>}], R = http_server:route(Req, [], nx_kernel), [_, _, {body, B}] = R, http_server:match_prefix(<<123,34,111,117,116,98,111,120,34,58,34,97,108,105,99,101,34,44,34,116,105,112,34,58,48>>, B) =/= nomatch\") :name)")
|
|
|
|
;; route/3 with kernel + SX Accept -> SX body carries :tip
|
|
(epoch 55)
|
|
(eval "(get (erlang-eval-ast \"AK = <<1,2,3,4>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], nx_kernel:start_link(alice, AKS, AAS), AcceptKey = <<97,99,99,101,112,116>>, AcceptVal = <<97,112,112,108,105,99,97,116,105,111,110,47,115,120>>, Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,111,117,116,98,111,120>>}, {headers, [{AcceptKey, AcceptVal}]}, {body, <<>>}], R = http_server:route(Req, [], nx_kernel), [_, _, {body, B}] = R, http_server:match_prefix(<<40,111,117,116,98,111,120,32,34,97,108,105,99,101,34,32,58,116,105,112,32,48,41>>, B) =/= nomatch\") :name)")
|
|
|
|
;; route/3 with kernel + multi-actor: bob's outbox tip is independent
|
|
(epoch 56)
|
|
(eval "(get (erlang-eval-ast \"AK = <<1,2,3,4>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], BK = <<5,6,7,8>>, BKS = [{key_id,k1},{algorithm,ed25519},{value,BK}], BAS = [{public_keys,[[{id,k1},{created,0},{value,BK}]]}], nx_kernel:start_link(alice, AKS, AAS), nx_kernel:add_actor(bob, BKS, BAS), AliceTok = <<97,108,105,99,101,45,116,111,107,101,110>>, AuthKey = <<97,117,116,104,111,114,105,122,97,116,105,111,110>>, AliceAuth = <<66,101,97,114,101,114,32,97,108,105,99,101,45,116,111,107,101,110>>, Cfg = [{tokens, [{AliceTok, alice}]}], PostReq = [{method, <<80,79,83,84>>}, {path, http_server:activity_path()}, {headers, [{AuthKey, AliceAuth}]}, {body, <<104,105>>}], http_server:route(PostReq, Cfg, nx_kernel), GetReq = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,98,111,98,47,111,117,116,98,111,120>>}, {headers, []}, {body, <<>>}], R = http_server:route(GetReq, [], nx_kernel), [_, _, {body, B}] = R, http_server:match_prefix(<<111,117,116,98,111,120,58,32,98,111,98,10,116,105,112,58,32,48>>, B) =/= nomatch\") :name)")
|
|
|
|
;; route/2 path (no kernel arg) still returns the 4a stub — back-compat
|
|
(epoch 57)
|
|
(eval "(get (erlang-eval-ast \"AK = <<1,2,3,4>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], nx_kernel:start_link(alice, AKS, AAS), Req = [{method, <<71,69,84>>}, {path, <<47,97,99,116,111,114,115,47,97,108,105,99,101,47,111,117,116,98,111,120>>}, {headers, []}, {body, <<>>}], R = http_server:route(Req, []), [_, _, {body, B}] = R, http_server:match_prefix(<<116,105,112,58>>, B) =:= nomatch\") :name)")
|
|
|
|
;; Token resolution before kernel is registered -> auth-stub published response
|
|
(epoch 47)
|
|
(eval "(get (erlang-eval-ast \"AliceTok = <<97,108,105,99,101,45,116,111,107,101,110>>, AuthKey = <<97,117,116,104,111,114,105,122,97,116,105,111,110>>, AliceAuth = <<66,101,97,114,101,114,32,97,108,105,99,101,45,116,111,107,101,110>>, Cfg = [{tokens, [{AliceTok, alice}]}], Req = [{method, <<80,79,83,84>>}, {path, http_server:activity_path()}, {headers, [{AuthKey, AliceAuth}]}, {body, <<>>}], R = http_server:route(Req, Cfg), case R of [{status, 200}, _, {body, B}] -> http_server:match_prefix(<<112,117,98,108,105,115,104,101,100>>, B) =/= nomatch; _ -> false end\") :name)")
|
|
EPOCHS
|
|
|
|
OUTPUT=$(timeout 240 "$SX_SERVER" < "$TMPFILE" 2>/dev/null)
|
|
|
|
check() {
|
|
local epoch="$1" desc="$2" expected="$3"
|
|
local actual
|
|
actual=$(echo "$OUTPUT" | awk -v e="$epoch" '
|
|
$0 ~ "^\\(ok-len " e " " { getline; print; exit }
|
|
$0 ~ "^\\(ok " e " " { print; exit }
|
|
$0 ~ "^\\(error " e " " { print; exit }
|
|
')
|
|
[ -z "$actual" ] && actual="<no output for epoch $epoch>"
|
|
if echo "$actual" | grep -qF -- "$expected"; then
|
|
PASS=$((PASS+1))
|
|
[ "$VERBOSE" = "-v" ] && echo " ok $desc"
|
|
else
|
|
FAIL=$((FAIL+1))
|
|
ERRORS+=" FAIL [$desc] (epoch $epoch) expected: $expected | actual: $actual
|
|
"
|
|
fi
|
|
}
|
|
|
|
check 2 "http_server loaded" "http_server"
|
|
check 10 "split sans slash returns bare" "true"
|
|
check 11 "split id/sub returns {id, sub}" "true"
|
|
check 12 "split id/ returns {id, <<>>}" "true"
|
|
check 20 "GET /actors/<id> regression" "true"
|
|
check 21 "GET /actors/<id>/outbox stub" "true"
|
|
check 22 "GET /actors/<id>/inbox stub" "true"
|
|
check 23 "GET /actors/<id>/followers stub" "true"
|
|
check 24 "GET /actors/<id>/following stub" "true"
|
|
check 25 "POST /actors/<id>/inbox -> 202" "true"
|
|
check 26 "GET /actors/<id>/<bad> -> 404" "true"
|
|
check 27 "POST /actors/<id>/<bad> -> 404" "true"
|
|
check 28 "GET /actors/ (empty) -> 404" "true"
|
|
check 29 "outbox body carries actor id" "true"
|
|
check 30 "outbox JSON content negotiation" "true"
|
|
check 31 "inbox SX content negotiation" "true"
|
|
check 32 "followers JSON content negotiation" "true"
|
|
check 40 "two-token Cfg + Alice POST -> 200" "true"
|
|
check 41 "Alice token publishes to alice" "true"
|
|
check 42 "Bob token publishes to bob" "true"
|
|
check 43 "interleaved tokens isolate logs" "true"
|
|
check 44 "unknown token -> 401" "true"
|
|
check 45 "legacy :publish_token still works" "true"
|
|
check 46 "tokens map + legacy back-compat" "true"
|
|
check 47 "no kernel + token map -> stub 200" "true"
|
|
check 50 "route/3 outbox includes tip = 0" "true"
|
|
check 51 "tip advances after publish" "true"
|
|
check 52 "unknown actor -> stub fallback" "true"
|
|
check 53 "unregistered kernel -> stub" "true"
|
|
check 54 "JSON outbox carries tip field" "true"
|
|
check 55 "SX outbox carries :tip field" "true"
|
|
check 56 "Bob outbox tip independent" "true"
|
|
check 57 "route/2 unchanged (no tip)" "true"
|
|
|
|
TOTAL=$((PASS+FAIL))
|
|
if [ $FAIL -eq 0 ]; then
|
|
echo "ok $PASS/$TOTAL next/tests/http_multi_actor.sh passed"
|
|
else
|
|
echo "FAIL $PASS/$TOTAL passed, $FAIL failed:"
|
|
echo "$ERRORS"
|
|
fi
|
|
[ $FAIL -eq 0 ]
|