archy/scripts/test-all-features.sh

196 lines
8.7 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
#
# test-all-features.sh — Comprehensive single-node feature validation
#
# Usage: ./scripts/test-all-features.sh [TARGET_IP] [--iterations N]
#
# Runs all feature checks on a single node:
# - System health (CPU, RAM, disk, services)
# - Container lifecycle (running, count, health monitor)
# - Federation (peers, trust, sync)
# - Tor (hidden service reachable)
# - DWN (status, write, query)
# - NIP-07 (provider injection)
# - Backup (create, list, verify, delete)
# - Identity (DID, credentials)
# - Monitoring (metrics endpoint)
#
# Exit 0 = all checks passed = production ready
# Output: TAP format
set -uo pipefail
TARGET="${1:-192.168.1.228}"
ITERATIONS=10
SSH_KEY="${HOME}/.ssh/archipelago-deploy"
SSH_OPTS="-i ${SSH_KEY} -o StrictHostKeyChecking=no -o ConnectTimeout=10"
SUDO_PASS="EwPDR8q45l0Upx@"
PASS=0
FAIL=0
TEST_NUM=0
# Parse args
shift || true
while [[ $# -gt 0 ]]; do
case "$1" in
--iterations) ITERATIONS="$2"; shift 2 ;;
*) echo "Unknown arg: $1"; exit 1 ;;
esac
done
tap_ok() { TEST_NUM=$((TEST_NUM+1)); PASS=$((PASS+1)); echo "ok ${TEST_NUM} - $1"; }
tap_fail() { TEST_NUM=$((TEST_NUM+1)); FAIL=$((FAIL+1)); echo "not ok ${TEST_NUM} - $1"; echo "# $2"; }
ssh_cmd() { ssh ${SSH_OPTS} "archipelago@${TARGET}" "$@" 2>/dev/null; }
ssh_sudo() { ssh ${SSH_OPTS} "archipelago@${TARGET}" "echo '${SUDO_PASS}' | sudo -S $*" 2>/dev/null; }
get_session() {
curl -s -D- -o/dev/null -X POST \
-H "Content-Type: application/json" \
-d '{"method":"auth.login","params":{"password":"password123"}}' \
"http://${TARGET}:5678/rpc/v1" 2>/dev/null | grep -i "set-cookie" | tr '\r' '\n'
}
rpc() {
local method="$1"
local params="${2:-\{\}}"
local session="$3"
local csrf="$4"
local timeout="${5:-30}"
local body
if [[ "$params" == "{}" || "$params" == "\{\}" ]]; then
body="{\"method\":\"${method}\"}"
else
body="{\"method\":\"${method}\",\"params\":${params}}"
fi
curl -s --max-time "$timeout" -X POST \
-H "Content-Type: application/json" \
-H "Cookie: session=${session}; csrf_token=${csrf}" \
-H "X-CSRF-Token: ${csrf}" \
-d "$body" \
"http://${TARGET}:5678/rpc/v1" 2>/dev/null
}
echo "TAP version 13"
echo "# Archipelago Full Feature Validation"
echo "# Target: ${TARGET}"
echo "# Iterations: ${ITERATIONS}"
echo "# Started: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
# ── Auth ──────────────────────────────────────────────────────────────────
session_header=$(get_session)
SESSION=$(echo "$session_header" | sed -n 's/.*session=\([^;]*\).*/\1/p' | head -1 | tr -d '[:space:]')
CSRF=$(echo "$session_header" | sed -n 's/.*csrf_token=\([^;]*\).*/\1/p' | head -1 | tr -d '[:space:]')
if [[ -z "$SESSION" ]]; then
echo "BAIL OUT! Cannot authenticate to ${TARGET}"
exit 1
fi
for i in $(seq 1 "$ITERATIONS"); do
echo ""
echo "# ── Iteration ${i}/${ITERATIONS} [$(date +%H:%M:%S)] ──"
# ── System Health ─────────────────────────────────────────────────────
health=$(curl -s --max-time 5 "http://${TARGET}/health" 2>/dev/null || echo "fail")
if [[ "$health" == "OK" ]]; then tap_ok "health-${i}"; else tap_fail "health-${i}" "$health"; fi
mem_avail=$(ssh_cmd "awk '/MemAvailable/ {print \$2}' /proc/meminfo" 2>/dev/null | tr -d '[:space:]')
if [[ -n "$mem_avail" ]] && [[ "$mem_avail" -gt 524288 ]]; then
tap_ok "memory-${i} # ${mem_avail}kB"
else
tap_fail "memory-${i}" "Available: ${mem_avail:-unknown}kB"
fi
disk=$(ssh_cmd "df / --output=pcent | tail -1" 2>/dev/null | tr -d '[:space:]%')
if [[ -n "$disk" ]] && [[ "$disk" -lt 85 ]]; then
tap_ok "disk-${i} # ${disk}%"
else
tap_fail "disk-${i}" "${disk:-unknown}%"
fi
# ── Containers ────────────────────────────────────────────────────────
count=$(ssh_sudo "podman ps --format '{{.Names}}' | wc -l" 2>/dev/null | tail -1 | tr -d '[:space:]')
if [[ -n "$count" ]] && [[ "$count" -ge 20 ]]; then
tap_ok "containers-${i} # ${count}"
else
tap_fail "containers-${i}" "Only ${count:-0}"
fi
exited=$(ssh_sudo "podman ps -a --format '{{.State}}' | grep -c -i exited" 2>/dev/null || echo "0")
exited=$(echo "$exited" | tail -1 | tr -d '[:space:]')
if [[ "$exited" == "0" ]]; then
tap_ok "no-exited-${i}"
else
tap_fail "no-exited-${i}" "${exited} exited"
fi
# ── Federation ────────────────────────────────────────────────────────
fed_result=$(rpc "federation.list-nodes" "{}" "$SESSION" "$CSRF")
peer_count=$(echo "$fed_result" | python3 -c "import sys,json; d=json.load(sys.stdin); print(len(d.get('result',{}).get('nodes',[])))" 2>/dev/null || echo "0")
if [[ "$peer_count" -ge 1 ]]; then
tap_ok "federation-peers-${i} # ${peer_count}"
else
tap_fail "federation-peers-${i}" "No peers"
fi
# ── DWN ───────────────────────────────────────────────────────────────
dwn_result=$(rpc "dwn.status" "{}" "$SESSION" "$CSRF")
dwn_running=$(echo "$dwn_result" | python3 -c "import sys,json; d=json.load(sys.stdin); print('yes' if d.get('result',{}).get('running') else 'no')" 2>/dev/null || echo "error")
if [[ "$dwn_running" == "yes" ]]; then
tap_ok "dwn-running-${i}"
else
tap_fail "dwn-running-${i}" "DWN not running"
fi
# ── Identity ──────────────────────────────────────────────────────────
did_result=$(rpc "node.did" "{}" "$SESSION" "$CSRF")
did=$(echo "$did_result" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('result',{}).get('did',''))" 2>/dev/null || echo "")
if [[ "$did" == did:* ]]; then
tap_ok "identity-did-${i} # ${did:0:20}..."
else
tap_fail "identity-did-${i}" "No DID: ${did}"
fi
# ── NIP-07 ────────────────────────────────────────────────────────────
provider=$(curl -s --connect-timeout 5 "http://${TARGET}/app/mempool/" 2>/dev/null | grep -c "nostr-provider" || echo "0")
if [[ "$provider" -gt 0 ]]; then
tap_ok "nip07-provider-${i}"
else
tap_fail "nip07-provider-${i}" "nostr-provider.js not found"
fi
# ── Backup (only first iteration to avoid rate limits) ────────────────
if [[ "$i" -eq 1 ]]; then
bk_pass="test-allfeatures-$(date +%s)"
bk_result=$(rpc "backup.create" "{\"passphrase\":\"${bk_pass}\",\"description\":\"allfeatures-test\"}" "$SESSION" "$CSRF" 60)
bk_id=$(echo "$bk_result" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('result',{}).get('id',''))" 2>/dev/null || echo "")
if [[ -n "$bk_id" && "$bk_id" != "None" ]]; then
tap_ok "backup-create # ${bk_id:0:8}"
# Verify
vr=$(rpc "backup.verify" "{\"id\":\"${bk_id}\",\"passphrase\":\"${bk_pass}\"}" "$SESSION" "$CSRF" 60)
valid=$(echo "$vr" | python3 -c "import sys,json; d=json.load(sys.stdin); print('yes' if d.get('result',{}).get('valid') else 'no')" 2>/dev/null || echo "no")
if [[ "$valid" == "yes" ]]; then tap_ok "backup-verify"; else tap_fail "backup-verify" "$vr"; fi
# Delete
rpc "backup.delete" "{\"id\":\"${bk_id}\"}" "$SESSION" "$CSRF" 10 >/dev/null 2>&1
tap_ok "backup-delete"
else
tap_fail "backup-create" "${bk_result:0:100}"
fi
fi
done
echo ""
TOTAL=$((PASS + FAIL))
echo "1..${TOTAL}"
echo ""
echo "# ═══════════════════════════════════════════════════════════════"
echo "# Results: ${PASS} passed, ${FAIL} failed, ${TOTAL} total"
echo "# Finished: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "# ═══════════════════════════════════════════════════════════════"
if [[ "$FAIL" -gt 0 ]]; then
exit 1
fi
exit 0