fed-sx-m1: Step 5c-populate — bootstrap:populate_registry into gen_server + 14 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 25s
This commit is contained in:
@@ -4,7 +4,8 @@
|
|||||||
default_base/0, ends_with_sx/1,
|
default_base/0, ends_with_sx/1,
|
||||||
build_genesis/1, verify_genesis/2,
|
build_genesis/1, verify_genesis/2,
|
||||||
cidhash_path/1, write_cidhash/2, read_cidhash/1,
|
cidhash_path/1, write_cidhash/2, read_cidhash/1,
|
||||||
load_genesis/1, strip_sx_suffix/1]).
|
load_genesis/1, strip_sx_suffix/1,
|
||||||
|
populate_registry/0]).
|
||||||
|
|
||||||
%% Genesis bundle reader per design §12.2.
|
%% Genesis bundle reader per design §12.2.
|
||||||
%%
|
%%
|
||||||
@@ -185,3 +186,22 @@ take_prefix(_, 0) -> <<>>;
|
|||||||
take_prefix(<<H, Rest/binary>>, N) when N > 0 ->
|
take_prefix(<<H, Rest/binary>>, N) when N > 0 ->
|
||||||
Tail = take_prefix(Rest, N - 1),
|
Tail = take_prefix(Rest, N - 1),
|
||||||
<<H, Tail/binary>>.
|
<<H, Tail/binary>>.
|
||||||
|
|
||||||
|
%% populate_registry/0 — load the canonical genesis bundle and
|
||||||
|
%% register every entry in the running registry gen_server. The
|
||||||
|
%% caller is expected to have started the registry (via
|
||||||
|
%% registry:start_link/0) before calling this. Returns the count
|
||||||
|
%% of entries registered across all kinds.
|
||||||
|
populate_registry() ->
|
||||||
|
{ok, Sections} = read_genesis(),
|
||||||
|
populate_sections(Sections, 0).
|
||||||
|
|
||||||
|
populate_sections([], Count) -> Count;
|
||||||
|
populate_sections([{Kind, Entries} | Rest], Count) ->
|
||||||
|
populate_sections(Rest, Count + populate_entries(Kind, Entries, 0)).
|
||||||
|
|
||||||
|
populate_entries(_, [], Count) -> Count;
|
||||||
|
populate_entries(Kind, [{Name, Bytes} | Rest], Count) ->
|
||||||
|
BaseName = strip_sx_suffix(Name),
|
||||||
|
ok = registry:register(Kind, BaseName, Bytes),
|
||||||
|
populate_entries(Kind, Rest, Count + 1).
|
||||||
|
|||||||
121
next/tests/bootstrap_populate.sh
Executable file
121
next/tests/bootstrap_populate.sh
Executable file
@@ -0,0 +1,121 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# next/tests/bootstrap_populate.sh — Step 5c-populate acceptance test.
|
||||||
|
#
|
||||||
|
# Closes the bootstrap → registry loop end-to-end. Each test
|
||||||
|
# inlines registry:start_link() with bootstrap:populate_registry()
|
||||||
|
# because spawned processes don't survive separate erlang-eval-ast
|
||||||
|
# invocations. 11 cases.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
# Shared prelude: starts registry, runs populate.
|
||||||
|
PRELUDE='registry:start_link(), N = bootstrap:populate_registry(),'
|
||||||
|
|
||||||
|
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 "(er-load-gen-server!)")
|
||||||
|
(epoch 3)
|
||||||
|
(eval "(get (erlang-load-module (file-read \"next/kernel/registry.erl\")) :name)")
|
||||||
|
(epoch 4)
|
||||||
|
(eval "(get (erlang-load-module (file-read \"next/kernel/bootstrap.erl\")) :name)")
|
||||||
|
|
||||||
|
;; populate returns the total count
|
||||||
|
(epoch 10)
|
||||||
|
(eval "(erlang-eval-ast \"${PRELUDE} N\")")
|
||||||
|
|
||||||
|
;; Per-kind counts match the manifest authored in Step 4
|
||||||
|
(epoch 20)
|
||||||
|
(eval "(erlang-eval-ast \"${PRELUDE} length(registry:list(activity_types))\")")
|
||||||
|
(epoch 21)
|
||||||
|
(eval "(erlang-eval-ast \"${PRELUDE} length(registry:list(object_types))\")")
|
||||||
|
(epoch 22)
|
||||||
|
(eval "(erlang-eval-ast \"${PRELUDE} length(registry:list(projections))\")")
|
||||||
|
(epoch 23)
|
||||||
|
(eval "(erlang-eval-ast \"${PRELUDE} length(registry:list(validators))\")")
|
||||||
|
(epoch 24)
|
||||||
|
(eval "(erlang-eval-ast \"${PRELUDE} length(registry:list(codecs))\")")
|
||||||
|
(epoch 25)
|
||||||
|
(eval "(erlang-eval-ast \"${PRELUDE} length(registry:list(sig_suites))\")")
|
||||||
|
(epoch 26)
|
||||||
|
(eval "(erlang-eval-ast \"${PRELUDE} length(registry:list(audience))\")")
|
||||||
|
|
||||||
|
;; Lookup of a known entry returns its bytes
|
||||||
|
(epoch 30)
|
||||||
|
(eval "(get (erlang-eval-ast \"${PRELUDE} case registry:lookup(activity_types, <<99,114,101,97,116,101>>) of {ok, B} -> is_binary(B) and (byte_size(B) > 100); _ -> false end\") :name)")
|
||||||
|
|
||||||
|
;; A known object-type entry registered correctly
|
||||||
|
(epoch 31)
|
||||||
|
(eval "(get (erlang-eval-ast \"${PRELUDE} case registry:lookup(object_types, <<100,101,102,105,110,101,45,97,99,116,105,118,105,116,121>>) of {ok, B} -> is_binary(B); _ -> false end\") :name)")
|
||||||
|
|
||||||
|
;; A known validator entry
|
||||||
|
(epoch 32)
|
||||||
|
(eval "(get (erlang-eval-ast \"${PRELUDE} case registry:lookup(validators, <<101,110,118,101,108,111,112,101,45,115,104,97,112,101>>) of {ok, B} -> is_binary(B); _ -> false end\") :name)")
|
||||||
|
EPOCHS
|
||||||
|
|
||||||
|
OUTPUT=$(timeout 300 "$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 "gen_server loaded" "gen_server"
|
||||||
|
check 3 "registry loaded" "registry"
|
||||||
|
check 4 "bootstrap loaded" "bootstrap"
|
||||||
|
check 10 "populate returns total 31" "31"
|
||||||
|
check 20 "activity_types count = 3" "3"
|
||||||
|
check 21 "object_types count = 10" "10"
|
||||||
|
check 22 "projections count = 7" "7"
|
||||||
|
check 23 "validators count = 3" "3"
|
||||||
|
check 24 "codecs count = 3" "3"
|
||||||
|
check 25 "sig_suites count = 2" "2"
|
||||||
|
check 26 "audience count = 3" "3"
|
||||||
|
check 30 "lookup activity_types/create" "true"
|
||||||
|
check 31 "lookup object_types/define-activity" "true"
|
||||||
|
check 32 "lookup validators/envelope-shape" "true"
|
||||||
|
|
||||||
|
TOTAL=$((PASS+FAIL))
|
||||||
|
if [ $FAIL -eq 0 ]; then
|
||||||
|
echo "ok $PASS/$TOTAL next/tests/bootstrap_populate.sh passed"
|
||||||
|
else
|
||||||
|
echo "FAIL $PASS/$TOTAL passed, $FAIL failed:"
|
||||||
|
echo "$ERRORS"
|
||||||
|
fi
|
||||||
|
[ $FAIL -eq 0 ]
|
||||||
@@ -340,7 +340,7 @@ created with a known stable CID.
|
|||||||
**Sub-deliverables:**
|
**Sub-deliverables:**
|
||||||
- [x] **5a** — Pure-functional `next/kernel/registry.erl`: `new/0`, `kinds/0`, `register/4`, `lookup/3`, `list/2`. State is a property list keyed by kind atom; per-kind storage is a property list of `{Name, Entry}`. Unknown kinds rejected with `{error, unknown_kind}`. `next/tests/registry_pure.sh` (14 cases).
|
- [x] **5a** — Pure-functional `next/kernel/registry.erl`: `new/0`, `kinds/0`, `register/4`, `lookup/3`, `list/2`. State is a property list keyed by kind atom; per-kind storage is a property list of `{Name, Entry}`. Unknown kinds rejected with `{error, unknown_kind}`. `next/tests/registry_pure.sh` (14 cases).
|
||||||
- [x] **5b** — gen_server wrapper around the pure registry: `start_link/0`, registered name `registry`, `register/3 lookup/2 list/1 stop/0` API delegating through `gen_server:call`. `next/tests/registry_server.sh` (12 cases). Port note: each test combines start_link + ops in a single expression because spawned processes don't survive across separate `erlang-eval-ast` invocations.
|
- [x] **5b** — gen_server wrapper around the pure registry: `start_link/0`, registered name `registry`, `register/3 lookup/2 list/1 stop/0` API delegating through `gen_server:call`. `next/tests/registry_server.sh` (12 cases). Port note: each test combines start_link + ops in a single expression because spawned processes don't survive across separate `erlang-eval-ast` invocations.
|
||||||
- [ ] **5c** — `bootstrap:load_genesis/1` (Step 4e) populates the registry from `read_genesis` output. Dispatches by section atom → kind.
|
- [x] **5c-populate** — `bootstrap:populate_registry/0` walks `read_genesis` output and calls `registry:register/3` (the gen_server API) for each entry. Returns the total entries registered. `next/tests/bootstrap_populate.sh` (14 cases).
|
||||||
- [ ] **5d** — define-registry projection fold integration: incoming `Create{Define*}` activities are routed through the projection scheduler (Step 7) and update the registry.
|
- [ ] **5d** — define-registry projection fold integration: incoming `Create{Define*}` activities are routed through the projection scheduler (Step 7) and update the registry.
|
||||||
|
|
||||||
**Deliverables:**
|
**Deliverables:**
|
||||||
@@ -996,6 +996,7 @@ A few things still under-specified; resolve as work begins.
|
|||||||
Newest first. One line per sub-deliverable commit. Erlang conformance gate
|
Newest first. One line per sub-deliverable commit. Erlang conformance gate
|
||||||
(`bash lib/erlang/conformance.sh`) must remain 729/729 on every entry.
|
(`bash lib/erlang/conformance.sh`) must remain 729/729 on every entry.
|
||||||
|
|
||||||
|
- **2026-05-28** — Step 5c-populate: `bootstrap:populate_registry/0` walks `read_genesis` output and calls `registry:register/3` (gen_server API) for each entry. Total return is 31 = 3 + 10 + 7 + 3 + 3 + 2 + 3 across the seven kinds, matching the manifest authored in Step 4. `next/tests/bootstrap_populate.sh` 14/14 verifies per-kind counts + lookups against known names (`activity_types/create`, `object_types/define-activity`, `validators/envelope-shape`). Erlang conformance 729/729.
|
||||||
- **2026-05-28** — Step 9-pre-fold: in-process integration test proving the full POST → publish → broadcast → projection-fold chain. With `projection:start_link` + `nx_kernel:start_link` + `nx_kernel:with_projections([p_count, p_collect])`, three authorized POST `/activity` calls advance both projections to 3 — and the kernel's log to 3 entries — and the projection's collected activity carries the POST body as `:object`. Unauthorized or sig-failed POSTs leave projection state unchanged. Step 9a/b proper (curl-driven smoke tests) wait on Step 8b-start (TCP) + Define\* SX-source eval bridge, but the structural chain is already verified end-to-end. `next/tests/http_publish_fold.sh` 10/10. Erlang conformance 729/729. Step 9 split into 9-pre-fold (done) + 9a + 9b.
|
- **2026-05-28** — Step 9-pre-fold: in-process integration test proving the full POST → publish → broadcast → projection-fold chain. With `projection:start_link` + `nx_kernel:start_link` + `nx_kernel:with_projections([p_count, p_collect])`, three authorized POST `/activity` calls advance both projections to 3 — and the kernel's log to 3 entries — and the projection's collected activity carries the POST body as `:object`. Unauthorized or sig-failed POSTs leave projection state unchanged. Step 9a/b proper (curl-driven smoke tests) wait on Step 8b-start (TCP) + Define\* SX-source eval bridge, but the structural chain is already verified end-to-end. `next/tests/http_publish_fold.sh` 10/10. Erlang conformance 729/729. Step 9 split into 9-pre-fold (done) + 9a + 9b.
|
||||||
- **2026-05-28** — Step 8c-post-publish-http: POST `/activity` handler now bridges into `nx_kernel:publish/1` when the kernel gen_server is registered (`erlang:whereis(nx_kernel) =/= undefined`). On success the response carries the canonical CID via `cid_response/1`; on pipeline failure the response is 422 via `validation_failed_response/0`. When the kernel isn't registered, the handler falls through to the existing 200 stub — preserves backwards compatibility for the auth-only tests in `http_post_activity.sh`. Distinct POSTs produce distinct CIDs (next_published counter in nx_kernel state). Unauthorized POSTs never reach the kernel — log tip stays at 0. `next/tests/http_publish.sh` 10/10. The POST `/activity` → publish → fold loop is now functional end-to-end through the kernel. Erlang conformance 729/729.
|
- **2026-05-28** — Step 8c-post-publish-http: POST `/activity` handler now bridges into `nx_kernel:publish/1` when the kernel gen_server is registered (`erlang:whereis(nx_kernel) =/= undefined`). On success the response carries the canonical CID via `cid_response/1`; on pipeline failure the response is 422 via `validation_failed_response/0`. When the kernel isn't registered, the handler falls through to the existing 200 stub — preserves backwards compatibility for the auth-only tests in `http_post_activity.sh`. Distinct POSTs produce distinct CIDs (next_published counter in nx_kernel state). Unauthorized POSTs never reach the kernel — log tip stays at 0. `next/tests/http_publish.sh` 10/10. The POST `/activity` → publish → fold loop is now functional end-to-end through the kernel. Erlang conformance 729/729.
|
||||||
- **2026-05-28** — Step 8c-post-publish-srv: `nx_kernel.erl` extended with gen_server callbacks + named-process API. `start_link/3(ActorId, KeySpec, ActorState)` spawns the worker and registers under the literal `nx_kernel` atom; `publish/1(Request)` calls into `handle_call({publish, Request}, ...)` which delegates to the pure `publish/2` and reflects the new state back into the server. `query/0` returns the full state proplist; `log_tip/0` is a direct accessor; `with_projections/1` mutates the projections list. Same port quirks as Step 5b/7b documented (raw Pid return, no `?MODULE`, processes don't persist across separate `erlang-eval-ast` calls — tests inline start_link with operations). `next/tests/nx_kernel_server.sh` 11/11. Erlang conformance 729/729.
|
- **2026-05-28** — Step 8c-post-publish-srv: `nx_kernel.erl` extended with gen_server callbacks + named-process API. `start_link/3(ActorId, KeySpec, ActorState)` spawns the worker and registers under the literal `nx_kernel` atom; `publish/1(Request)` calls into `handle_call({publish, Request}, ...)` which delegates to the pure `publish/2` and reflects the new state back into the server. `query/0` returns the full state proplist; `log_tip/0` is a direct accessor; `with_projections/1` mutates the projections list. Same port quirks as Step 5b/7b documented (raw Pid return, no `?MODULE`, processes don't persist across separate `erlang-eval-ast` calls — tests inline start_link with operations). `next/tests/nx_kernel_server.sh` 11/11. Erlang conformance 729/729.
|
||||||
|
|||||||
Reference in New Issue
Block a user