diff --git a/tests/lifecycle/bats/required-stack.bats b/tests/lifecycle/bats/required-stack.bats index 096d70a7..f05f3880 100644 --- a/tests/lifecycle/bats/required-stack.bats +++ b/tests/lifecycle/bats/required-stack.bats @@ -1,7 +1,14 @@ #!/usr/bin/env bats # tests/lifecycle/bats/required-stack.bats # -# Read-only release-gate checks for the required Bitcoin stack on .116. +# Read-only release-gate checks for the Bitcoin/electrum/lnd/mempool stack. +# Originally written against .116's fixed app roster; the "present"/"running" +# checks below now only require containers actually installed on THIS node +# (podman_all_names — present in `podman ps -a` even if stopped), so a node +# with a different app subset (e.g. no mempool stack) doesn't hard-fail on +# apps it was never meant to have. Per-app checks further down (mempool, +# filebrowser, ...) skip individually if that app isn't installed, matching +# the mempool_skip_if_absent idiom in mempool.bats. # # This suite is intentionally non-destructive and does not use RPC auth; # it can run anytime as a health gate during long sync/reindex windows. @@ -19,15 +26,38 @@ required_containers=( "archy-electrs-ui" ) +fail() { echo "$@" >&2; return 1; } + podman_names() { podman ps --format '{{.Names}}' } +podman_all_names() { + podman ps -a --format '{{.Names}}' +} + container_running() { local name="$1" podman inspect --format '{{.State.Running}}' "$name" 2>/dev/null } +container_installed() { + local name="$1" + podman_all_names | grep -Fx "$name" >/dev/null +} + +skip_if_not_installed() { + container_installed "$1" || skip "$1 not installed on this node" +} + +# The subset of required_containers actually installed on this node. +installed_required_containers() { + local c + for c in "${required_containers[@]}"; do + container_installed "$c" && echo "$c" + done +} + bitcoin_rpc() { curl -fsS --max-time 60 \ --user "archipelago:$(cat /var/lib/archipelago/secrets/bitcoin-rpc-password)" \ @@ -42,13 +72,16 @@ bitcoin_json() { @test "required containers are present" { # Under sustained 5× churn an app may still be mid-restart when this runs; - # wait for the whole required set rather than single-shot. + # wait for the whole required set rather than single-shot. Only checks + # containers actually installed on this node (see installed_required_containers). + local targets; targets="$(installed_required_containers)" + [[ -n "$targets" ]] || skip "none of required_containers installed on this node" local deadline=$(( $(date +%s) + 180 )) names missing while (( $(date +%s) < deadline )); do names="$(podman_names)"; missing="" - for c in "${required_containers[@]}"; do + while IFS= read -r c; do echo "$names" | grep -Fx "$c" >/dev/null || missing="$missing $c" - done + done <<< "$targets" [[ -z "$missing" ]] && return 0 sleep 3 done @@ -56,12 +89,14 @@ bitcoin_json() { } @test "required containers are running" { + local targets; targets="$(installed_required_containers)" + [[ -n "$targets" ]] || skip "none of required_containers installed on this node" local deadline=$(( $(date +%s) + 180 )) notrunning while (( $(date +%s) < deadline )); do notrunning="" - for c in "${required_containers[@]}"; do + while IFS= read -r c; do [[ "$(container_running "$c" 2>/dev/null)" == "true" ]] || notrunning="$notrunning $c" - done + done <<< "$targets" [[ -z "$notrunning" ]] && return 0 sleep 3 done @@ -69,12 +104,14 @@ bitcoin_json() { } @test "bitcoin-knots RPC responds" { + skip_if_not_installed bitcoin-knots run bitcoin_rpc [ "$status" -eq 0 ] echo "$output" | python3 -c 'import json,sys; r=json.load(sys.stdin)["result"]; assert r["chain"] == "main" and r["blocks"] >= 0' } @test "bitcoin backend is synced archival for electrumx/lnd gate" { + skip_if_not_installed bitcoin-knots run bitcoin_rpc [ "$status" -eq 0 ] @@ -95,6 +132,7 @@ bitcoin_json() { } @test "electrumx TCP port accepts connections" { + skip_if_not_installed electrumx run python3 - <<'PY' import socket s = socket.create_connection(("127.0.0.1", 50001), 3) @@ -105,6 +143,7 @@ PY } @test "lnd CLI getinfo succeeds" { + skip_if_not_installed lnd # lnd RPC readiness lags the container "running" state (wallet auto-unlock on # start), so retry until ready rather than single-shot. See lnd.bats note. run sh -lc 'for i in $(seq 1 30); do @@ -115,6 +154,7 @@ PY } @test "lnd REST port accepts connections" { + skip_if_not_installed lnd run python3 - <<'PY' import socket s = socket.create_connection(("127.0.0.1", 18080), 3) @@ -125,17 +165,20 @@ PY } @test "mempool api endpoint responds" { + skip_if_not_installed mempool-api # mempool-api reconnects to electrumx after a stack restart — retry ~180s. run sh -lc 'for i in $(seq 1 60); do curl -fsS -m 5 -o /dev/null "http://127.0.0.1:8999/api/v1/backend-info" && exit 0; sleep 3; done; exit 1' [ "$status" -eq 0 ] } @test "mempool frontend responds" { + skip_if_not_installed mempool run sh -lc 'for i in $(seq 1 60); do curl -fsS -m 5 -o /dev/null "http://127.0.0.1:4080/" && exit 0; sleep 3; done; exit 1' [ "$status" -eq 0 ] } @test "bitcoin ui responds" { + skip_if_not_installed archy-bitcoin-ui # The companion (archy-bitcoin-ui) may have just been recreated by an earlier # companion-survives test; its nginx takes a moment to serve. Retry ~120s # rather than single-shot. @@ -144,11 +187,13 @@ PY } @test "lnd ui responds" { + skip_if_not_installed archy-lnd-ui run curl -fsS "http://127.0.0.1:18083/" [ "$status" -eq 0 ] } @test "filebrowser responds" { + skip_if_not_installed filebrowser run curl -fsS "http://127.0.0.1:8083/" [ "$status" -eq 0 ] }