Files
rose-ash/next/tests/smoke_kernel_route.sh
giles 03c32cda5f
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 1m6s
fed-sx-m2: resolve Blockers #4 — kernel routes now work over real HTTP
Substrate fix: two-line change to lib/erlang/runtime.sx that lets
http-listen handler routes call gen_server:call without deadlocking.

  1. er-sched-step-alive!: pass :pending-args (when set) to the
     initial-fun call instead of always passing an empty list.
     Default behavior (no field) stays (list) — drop-in safe.

  2. er-bif-http-listen sx-handler: instead of er-apply-fun handler
     inline (which blows up on receive's er-suspend-marker because
     the connection thread has no scheduler step on its stack),
     create a real er-process with :initial-fun = handler and
     :pending-args = (list req-pl), then er-sched-run-all! to drain.
     Any receive (e.g. gen_server:call) suspends + resumes inside
     the SX scheduler frame the process owns. Read :exit-result
     for the response proplist; marshal back to SX dict.

Investigation arc (see plans/fed-sx-milestone-2.md Blockers #4 +
Progress log):
  - loops/fed-prims bf8d0bf2 diagnosed it as Erlang-substrate, not
    OCaml mutex (Pattern A wrong, Pattern B right but sketchy).
  - First Pattern B attempt failed: tried er-spawn-fun on a raw SX
    lambda, hit (er-fun? fv) gate. Connection-thread bisect
    pinpointed the exact line.
  - Real fix: use the existing er-fun (user's handler) directly,
    but feed it via :pending-args so step-alive's hardcoded
    (list) doesn't drop the request arg.

Acceptance:
  - new next/tests/smoke_kernel_route.sh: 6/6 over real HTTP
    (welcome /, /actors/alice, /actors/alice/outbox with
    gen_server-backed tip, /actors/alice/inbox, unknown-actor,
    via http_server:start(P, [{kernel, nx_kernel}])).
  - next/tests/http_server_tcp.sh: 5/5 (bumped wait_bound from
    30s to 180s — cold boot is slow under sibling-loop CPU load
    and the per-handler scheduler ramp adds a small margin).
  - Erlang conformance: 761/761.

Step 12's two-instance smoke test is now unblocked — its full
Follow / Accept / Note flow can layer on top of this kernel-route
surface. m2 plan updated.

Pre-existing httpc_request.sh flakiness ("Undefined symbol:
http-request" on the live-call epochs) reproduces WITHOUT this
change — see git stash A/B in the investigation. Unrelated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-07 20:04:19 +00:00

122 lines
4.2 KiB
Bash
Executable File

#!/usr/bin/env bash
# next/tests/smoke_kernel_route.sh — m2 Blockers #4 unblock test.
#
# Proves a real HTTP listener over http:listen + http_server:start
# CAN now serve kernel-aware routes (the surface Blockers #4 made
# unreachable). Spins up a single sx_server instance, bootstraps an
# actor, starts http_server with {kernel, nx_kernel} in Cfg, and
# curls a route that fans through nx_kernel via gen_server:call.
#
# This is the kernel-route portion of Step 12's two-instance smoke
# test. The full two-instance flow (Follow + auto-accept + Note
# delivery) layers on top of this surface; this test is the
# load-bearing proof point that the underlying wiring works.
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=""
PORT=$(python3 -c 'import socket;s=socket.socket();s.bind(("127.0.0.1",0));print(s.getsockname()[1]);s.close()')
EF=$(mktemp); LOG=$(mktemp); FIFO=$(mktemp -u); mkfifo "$FIFO"
cleanup() {
for pid in ${SXP:-} ${HOLDP:-}; do
kill -KILL "$pid" 2>/dev/null || true
wait "$pid" 2>/dev/null || true
done
rm -f "$EF" "$LOG" "$FIFO"
}
trap cleanup EXIT
cat > "$EF" <<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 "(er-load-gen-server!)")
(eval "(get (erlang-load-module (file-read \"next/kernel/envelope.erl\")) :name)")
(eval "(get (erlang-load-module (file-read \"next/kernel/log.erl\")) :name)")
(eval "(get (erlang-load-module (file-read \"next/kernel/pipeline.erl\")) :name)")
(eval "(get (erlang-load-module (file-read \"next/kernel/term_codec.erl\")) :name)")
(eval "(get (erlang-load-module (file-read \"next/kernel/outbox.erl\")) :name)")
(eval "(get (erlang-load-module (file-read \"next/kernel/nx_kernel.erl\")) :name)")
(eval "(get (erlang-load-module (file-read \"next/kernel/http_server.erl\")) :name)")
(epoch 3)
(eval "(erlang-eval-ast \"AK = <<1,1,1,1>>, AKS = [{key_id,k1},{algorithm,ed25519},{value,AK}], AAS = [{public_keys,[[{id,k1},{created,0},{value,AK}]]}], nx_kernel:start_link(alice, AKS, AAS), http_server:start(${PORT}, [{kernel, nx_kernel}])\")")
EPOCHS
( cat "$EF"; sleep 900 ) > "$FIFO" &
HOLDP=$!
"$SX_SERVER" < "$FIFO" > "$LOG" 2>&1 &
SXP=$!
rm -f "$FIFO"
START=$(date +%s)
BOUND=
while [ $(($(date +%s) - START)) -lt 300 ]; do
if (exec 3<>/dev/tcp/127.0.0.1/$PORT) 2>/dev/null; then
exec 3<&-; exec 3>&-
BOUND="yes after $(($(date +%s) - START))s"
break
fi
sleep 1
done
if [ -z "$BOUND" ]; then
echo "FAIL: listener never bound on port $PORT"
echo "--- log tail ---"
tail -20 "$LOG"
exit 1
fi
[ "$VERBOSE" = "-v" ] && echo " ok listener up ($BOUND)"
check() {
local desc="$1" path="$2" needle="$3"
local resp
resp=$(curl -s --max-time 10 "http://127.0.0.1:$PORT$path" 2>/dev/null || echo "<curl-failed>")
if echo "$resp" | grep -qF -- "$needle"; then
PASS=$((PASS+1))
[ "$VERBOSE" = "-v" ] && echo " ok $desc"
else
FAIL=$((FAIL+1))
ERRORS+=" FAIL [$desc] expected '$needle' in resp: $(echo "$resp" | head -c 100)
"
fi
}
check "non-kernel welcome /" "/" "fed-sx kernel m1"
check "kernel-aware /actors/alice" "/actors/alice" "actor: alice"
check "kernel-aware /actors/alice/outbox" "/actors/alice/outbox" "outbox: alice"
check "kernel-aware /actors/alice/outbox tip" "/actors/alice/outbox" "tip: 0"
check "kernel-aware /actors/alice/inbox" "/actors/alice/inbox" "inbox: alice"
check "unknown actor /actors/zzz/outbox" "/actors/zzz/outbox" "outbox: zzz"
TOTAL=$((PASS+FAIL))
if [ $FAIL -eq 0 ]; then
echo "ok $PASS/$TOTAL next/tests/smoke_kernel_route.sh passed (port $PORT)"
else
echo "FAIL $PASS/$TOTAL passed, $FAIL failed:"
echo "$ERRORS"
if [ "$VERBOSE" = "-v" ]; then
echo "--- log tail ---"; tail -20 "$LOG"
fi
fi
[ $FAIL -eq 0 ]