Same shape as bitcoin-knots.bats and lnd.bats so the 20× release-gate
exercises electrumx through the same state matrix it uses for the other
two core apps. electrumx previously had a single TCP-port check inside
required-stack.bats; this adds destructive + cascade-destructive tiers.
10 @test cases:
* read-only: presence, valid state, TCP port (50001) reachable, no
orphan containers beyond {electrumx, archy-electrs-ui}
* destructive: stop, start, restart, TCP port recovers within 120s of
cold restart (longer than bitcoind because electrumx replays its
index against bitcoind on start)
* cascade: uninstall, reinstall (240s timeout for index rebuild)
With this suite, the three single-container core apps (bitcoin-knots,
lnd, electrumx) now have parity coverage. Multi-container stacks
(btcpay, mempool, fedimint) come next.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
147 lines
5.6 KiB
Bash
147 lines
5.6 KiB
Bash
#!/usr/bin/env bats
|
||
# tests/lifecycle/bats/electrumx.bats
|
||
#
|
||
# Lifecycle tests for the electrumx package (containers are named
|
||
# `electrumx` + `archy-electrs-ui`). Mirrors bitcoin-knots.bats /
|
||
# lnd.bats so the 20× release-gate run exercises electrumx through
|
||
# the same state matrix.
|
||
#
|
||
# Tiers:
|
||
# - Read-only (always runs): presence, valid state, TCP reachable
|
||
# - Destructive (ARCHY_ALLOW_DESTRUCTIVE=1): stop → start → restart
|
||
# - Cascade-destructive (ARCHY_ALLOW_CASCADE_DESTRUCTIVE=1): uninstall → reinstall
|
||
#
|
||
# Pre-req: electrumx is installed and bitcoin-knots is running (electrumx
|
||
# depends on bitcoind RPC for headers).
|
||
|
||
load '../lib/rpc.bash'
|
||
|
||
setup_file() {
|
||
: "${ARCHY_PASSWORD:?Set ARCHY_PASSWORD env var to the UI password}"
|
||
export ARCHY_FORCE_LOGIN=1
|
||
rpc_login
|
||
unset ARCHY_FORCE_LOGIN
|
||
}
|
||
|
||
teardown_file() {
|
||
rpc_logout_local
|
||
}
|
||
|
||
# ────────────────────────────────────────────────────────────────────
|
||
# Read-only tier
|
||
# ────────────────────────────────────────────────────────────────────
|
||
|
||
@test "container-list includes electrumx" {
|
||
run rpc_result container-list
|
||
[ "$status" -eq 0 ]
|
||
echo "$output" | jq -e '.[] | select(.name == "electrumx")' >/dev/null
|
||
}
|
||
|
||
@test "container-list reports a valid state for electrumx" {
|
||
run rpc_result container-list
|
||
[ "$status" -eq 0 ]
|
||
local state
|
||
state=$(echo "$output" | jq -r '.[] | select(.name == "electrumx") | .state')
|
||
[[ "$state" =~ ^(running|stopped|exited|created|paused)$ ]]
|
||
}
|
||
|
||
@test "electrumx TCP port accepts connections when running" {
|
||
local state
|
||
state=$(rpc_result container-list | jq -r '.[] | select(.name == "electrumx") | .state')
|
||
if [[ "$state" != "running" ]]; then
|
||
skip "electrumx not running (state=$state)"
|
||
fi
|
||
|
||
# Same probe required-stack.bats uses — divergence flags a real regression.
|
||
run python3 - <<'PY'
|
||
import socket
|
||
s = socket.create_connection(("127.0.0.1", 50001), 3)
|
||
s.close()
|
||
print("ok")
|
||
PY
|
||
[ "$status" -eq 0 ]
|
||
}
|
||
|
||
@test "no orphan electrumx-related containers beyond the known set" {
|
||
# FM4 guard: known-good electrumx-package set is {electrumx, archy-electrs-ui}.
|
||
local total known
|
||
total=$(podman ps -a --format '{{.Names}}' \
|
||
| grep -Ec '^(electrumx|electrs|archy-electrs(-[a-z]+)?)$' || true)
|
||
known=$(podman ps -a --format '{{.Names}}' \
|
||
| grep -Ec '^(electrumx|archy-electrs-ui)$' || true)
|
||
[ "$total" -eq "$known" ]
|
||
}
|
||
|
||
# ────────────────────────────────────────────────────────────────────
|
||
# Destructive tier (stop → start → restart on the same container)
|
||
# ────────────────────────────────────────────────────────────────────
|
||
|
||
@test "package.stop transitions electrumx to stopped" {
|
||
[[ "${ARCHY_ALLOW_DESTRUCTIVE:-0}" == "1" ]] || skip "ARCHY_ALLOW_DESTRUCTIVE not set"
|
||
|
||
run rpc_result package.stop '{"id":"electrumx"}'
|
||
[ "$status" -eq 0 ]
|
||
|
||
run wait_for_container_status electrumx stopped 60
|
||
[ "$status" -eq 0 ]
|
||
}
|
||
|
||
@test "package.start brings electrumx back to running" {
|
||
[[ "${ARCHY_ALLOW_DESTRUCTIVE:-0}" == "1" ]] || skip "ARCHY_ALLOW_DESTRUCTIVE not set"
|
||
|
||
run rpc_result package.start '{"id":"electrumx"}'
|
||
[ "$status" -eq 0 ]
|
||
|
||
run wait_for_container_status electrumx running 120
|
||
[ "$status" -eq 0 ]
|
||
}
|
||
|
||
@test "package.restart leaves electrumx in running state" {
|
||
[[ "${ARCHY_ALLOW_DESTRUCTIVE:-0}" == "1" ]] || skip "ARCHY_ALLOW_DESTRUCTIVE not set"
|
||
|
||
run rpc_result package.restart '{"id":"electrumx"}'
|
||
[ "$status" -eq 0 ]
|
||
|
||
run wait_for_container_status electrumx running 120
|
||
[ "$status" -eq 0 ]
|
||
}
|
||
|
||
@test "electrumx TCP port recovers after restart" {
|
||
[[ "${ARCHY_ALLOW_DESTRUCTIVE:-0}" == "1" ]] || skip "ARCHY_ALLOW_DESTRUCTIVE not set"
|
||
|
||
# electrumx replays its index against bitcoind on cold start; allow 120s.
|
||
local deadline=$(( $(date +%s) + 120 ))
|
||
while (( $(date +%s) < deadline )); do
|
||
if python3 -c 'import socket; socket.create_connection(("127.0.0.1", 50001), 3).close()' \
|
||
>/dev/null 2>&1; then
|
||
return 0
|
||
fi
|
||
sleep 3
|
||
done
|
||
fail "electrumx TCP port never reopened after restart"
|
||
}
|
||
|
||
# ────────────────────────────────────────────────────────────────────
|
||
# Cascade-destructive tier (uninstall + reinstall)
|
||
# ────────────────────────────────────────────────────────────────────
|
||
|
||
@test "package.uninstall removes electrumx" {
|
||
[[ "${ARCHY_ALLOW_CASCADE_DESTRUCTIVE:-0}" == "1" ]] || skip "ARCHY_ALLOW_CASCADE_DESTRUCTIVE not set"
|
||
|
||
run rpc_result package.uninstall '{"id":"electrumx","preserve_data":true}'
|
||
[ "$status" -eq 0 ]
|
||
|
||
run wait_for_container_status electrumx absent 120
|
||
[ "$status" -eq 0 ]
|
||
}
|
||
|
||
@test "package.install electrumx returns to running" {
|
||
[[ "${ARCHY_ALLOW_CASCADE_DESTRUCTIVE:-0}" == "1" ]] || skip "ARCHY_ALLOW_CASCADE_DESTRUCTIVE not set"
|
||
|
||
run rpc_result package.install '{"manifest_path":"electrumx/manifest.yaml"}'
|
||
[ "$status" -eq 0 ]
|
||
|
||
run wait_for_container_status electrumx running 240
|
||
[ "$status" -eq 0 ]
|
||
}
|