archy/tests/lifecycle/bats/required-stack.bats

134 lines
3.4 KiB
Plaintext
Raw Normal View History

#!/usr/bin/env bats
# tests/lifecycle/bats/required-stack.bats
#
# Read-only release-gate checks for the required Bitcoin stack on .116.
#
# 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.
required_containers=(
"bitcoin-knots"
"electrumx"
"lnd"
2026-05-13 15:09:22 -04:00
"archy-mempool-db"
"mempool-api"
"mempool"
2026-05-13 15:09:22 -04:00
"filebrowser"
"archy-bitcoin-ui"
"archy-lnd-ui"
"archy-electrs-ui"
)
podman_names() {
podman ps --format '{{.Names}}'
}
container_running() {
local name="$1"
podman inspect --format '{{.State.Running}}' "$name" 2>/dev/null
}
2026-05-13 15:09:22 -04:00
bitcoin_rpc() {
curl -fsS --max-time 60 \
--user "archipelago:$(cat /var/lib/archipelago/secrets/bitcoin-rpc-password)" \
--data-binary '{"jsonrpc":"1.0","id":"required-stack","method":"getblockchaininfo","params":[]}' \
-H 'content-type: text/plain;' \
http://127.0.0.1:8332/
}
bitcoin_json() {
python3 -c 'import json,sys; r=json.load(sys.stdin)["result"]; print(r[sys.argv[1]])' "$1"
}
@test "required containers are present" {
local names
names="$(podman_names)"
for c in "${required_containers[@]}"; do
echo "$names" | grep -Fx "$c" >/dev/null
done
}
@test "required containers are running" {
for c in "${required_containers[@]}"; do
run container_running "$c"
[ "$status" -eq 0 ]
[ "$output" = "true" ]
done
}
@test "bitcoin-knots RPC responds" {
2026-05-13 15:09:22 -04:00
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" {
run bitcoin_rpc
[ "$status" -eq 0 ]
2026-05-13 15:09:22 -04:00
local pruned ibd blocks headers
pruned="$(echo "$output" | bitcoin_json pruned)"
ibd="$(echo "$output" | bitcoin_json initialblockdownload)"
blocks="$(echo "$output" | bitcoin_json blocks)"
headers="$(echo "$output" | bitcoin_json headers)"
if [ "$pruned" = "True" ] || [ "$pruned" = "true" ]; then
echo "bitcoin is pruned (blocks=$blocks headers=$headers); electrumx cannot index pruned historical blocks"
return 1
fi
if [ "$ibd" = "True" ] || [ "$ibd" = "true" ]; then
echo "bitcoin is still in initial block download (blocks=$blocks headers=$headers)"
return 1
fi
}
@test "electrumx TCP port accepts connections" {
run python3 - <<'PY'
import socket
s = socket.create_connection(("127.0.0.1", 50001), 3)
s.close()
print("ok")
PY
[ "$status" -eq 0 ]
}
@test "lnd CLI getinfo succeeds" {
2026-05-13 15:09:22 -04:00
run sh -lc 'timeout 60 podman exec lnd lncli --tlscertpath /root/.lnd/tls.cert --macaroonpath /root/.lnd/data/chain/bitcoin/mainnet/readonly.macaroon --rpcserver localhost:10009 getinfo >/dev/null'
[ "$status" -eq 0 ]
}
@test "lnd REST port accepts connections" {
run python3 - <<'PY'
import socket
s = socket.create_connection(("127.0.0.1", 18080), 3)
s.close()
print("ok")
PY
[ "$status" -eq 0 ]
}
@test "mempool api endpoint responds" {
run curl -fsS "http://127.0.0.1:8999/api/v1/backend-info"
[ "$status" -eq 0 ]
}
@test "mempool frontend responds" {
run curl -fsS "http://127.0.0.1:4080/"
[ "$status" -eq 0 ]
}
@test "bitcoin ui responds" {
run curl -fsS "http://127.0.0.1:8334/"
[ "$status" -eq 0 ]
}
@test "lnd ui responds" {
2026-05-13 15:09:22 -04:00
run curl -fsS "http://127.0.0.1:18083/"
[ "$status" -eq 0 ]
}
@test "filebrowser responds" {
run curl -fsS "http://127.0.0.1:8083/"
[ "$status" -eq 0 ]
}