From c4f24f3efa203cd6be45b633305514a2e4277150 Mon Sep 17 00:00:00 2001 From: archipelago Date: Tue, 28 Jul 2026 17:00:58 -0400 Subject: [PATCH] =?UTF-8?q?test(mesh):=20mesh/Reticulum=20test=20gate=20?= =?UTF-8?q?=E2=80=94=20unit=20tests=20+=20daemon=20selftest=20+=20live-nod?= =?UTF-8?q?e=20assertions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tests/mesh/run-mesh-tests.sh: cheap-first layers — 116 Rust mesh unit tests, the daemon --selftest (now also asserting the announce app_data wire contract and the set_name verb), and opt-in live assertions against a running node (radio connected, named, mesh.refresh/broadcast, no ARCHY-blob peer names). Verified green on archi-dev-box and archy-x250-dev. Co-Authored-By: Claude Fable 5 --- tests/mesh/run-mesh-tests.sh | 85 ++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100755 tests/mesh/run-mesh-tests.sh diff --git a/tests/mesh/run-mesh-tests.sh b/tests/mesh/run-mesh-tests.sh new file mode 100755 index 00000000..7911dd8e --- /dev/null +++ b/tests/mesh/run-mesh-tests.sh @@ -0,0 +1,85 @@ +#!/bin/bash +# Mesh / Reticulum test suite — the "is the mesh stack healthy" gate. +# +# Three layers, cheapest first: +# 1. Rust unit tests (no hardware, ~2s once built) +# 2. Daemon selftest (full RNS+LXMF bring-up, no radio; also verifies +# the announce app_data wire contract + set_name) +# 3. Live-node assertions (optional; needs a running archipelago with a +# radio — set MESH_TEST_LIVE=1 MESH_TEST_PW=...) +# +# Usage: +# tests/mesh/run-mesh-tests.sh # layers 1+2 +# MESH_TEST_LIVE=1 MESH_TEST_PW='...' tests/mesh/run-mesh-tests.sh +# MESH_TEST_HOST=100.113.100.55 ... # live-test a remote node +set -u +cd "$(dirname "$0")/../.." +FAIL=0 +ok() { echo "ok - $1"; } +bad() { echo "not ok - $1"; FAIL=1; } + +# ── 1. Rust unit tests ──────────────────────────────────────────────── +RUST_RESULTS=$(cd core && cargo test -p archipelago --bin archipelago mesh 2>&1 | grep "^test result:") +if [ -n "$RUST_RESULTS" ] && ! echo "$RUST_RESULTS" | grep -vq " 0 failed"; then + ok "rust mesh unit tests ($(echo "$RUST_RESULTS" | grep -o '[0-9]* passed' | head -1))" +else + bad "rust mesh unit tests" +fi + +# ── 2. Reticulum daemon selftest (no radio) ─────────────────────────── +TMP=$(mktemp -d) +trap 'rm -rf "$TMP"' EXIT +head -c 32 /dev/urandom > "$TMP/key" +DAEMON=reticulum-daemon/.venv/bin/python +if [ -x "$DAEMON" ]; then + if "$DAEMON" reticulum-daemon/reticulum_daemon.py \ + --identity-key "$TMP/key" --rns-config "$TMP/rns" \ + --socket "$TMP/sock" --display-name "SelftestNode" --selftest 2>/dev/null \ + | grep -q "announce_app_data=verified set_name=verified"; then + ok "daemon selftest (announce wire contract + set_name)" + else + bad "daemon selftest" + fi +else + echo "skip - daemon selftest (no venv at $DAEMON)" +fi + +# ── 3. Live node assertions (opt-in) ────────────────────────────────── +if [ "${MESH_TEST_LIVE:-0}" = "1" ]; then + HOST="${MESH_TEST_HOST:-127.0.0.1}" + PW="${MESH_TEST_PW:?set MESH_TEST_PW}" + # Nodes differ: dev boxes serve plain http on :80, ISO installs https. + RPC="" + for base in "http://$HOST" "https://$HOST" "http://$HOST:5678"; do + code=$(curl -ksS -o /dev/null -w '%{http_code}' -m 5 -X POST "$base/rpc/v1" 2>/dev/null || true) + case "$code" in 000|"") continue ;; *) RPC="$base/rpc/v1"; break ;; esac + done + [ -n "$RPC" ] || { bad "live: no RPC endpoint reachable on $HOST"; echo FAIL; exit 1; } + JAR="$TMP/jar" + curl -ksS -c "$JAR" -H "Content-Type: application/json" \ + -d "{\"jsonrpc\":\"2.0\",\"method\":\"auth.login\",\"params\":{\"password\":\"$PW\"},\"id\":1}" \ + "$RPC" > "$TMP/login" + if grep -q '"error":null' "$TMP/login"; then ok "live: rpc login"; else bad "live: rpc login"; fi + call() { + local csrf; csrf=$(awk '/^[^#]/ && /csrf_token/ {print $7; exit}' "$JAR") + curl -ksS -b "$JAR" -c "$JAR" -H "Content-Type: application/json" \ + -H "X-CSRF-Token: $csrf" \ + -d "{\"jsonrpc\":\"2.0\",\"method\":\"$1\",\"params\":${2:-{\}},\"id\":2}" \ + --max-time 60 "$RPC" + } + ST=$(call mesh.status) + echo "$ST" | grep -q '"device_connected":true' \ + && ok "live: radio connected ($(echo "$ST" | grep -o '"device_type":"[a-z]*"'))" \ + || bad "live: radio connected" + echo "$ST" | grep -q '"self_advert_name":"[^"]' \ + && ok "live: node has a mesh name" || bad "live: node has a mesh name" + call mesh.refresh | grep -q '"refreshed":true' \ + && ok "live: mesh.refresh" || bad "live: mesh.refresh" + call mesh.broadcast | grep -q '"broadcast":true' \ + && ok "live: mesh.broadcast" || bad "live: mesh.broadcast" + # No peer may ever display a raw identity blob as its name. + call mesh.peers | grep -q '"advert_name":"ARCHY:' \ + && bad "live: no ARCHY-blob peer names" || ok "live: no ARCHY-blob peer names" +fi + +[ "$FAIL" = 0 ] && echo "PASS" || { echo "FAIL"; exit 1; }