archy/scripts/test-app-install.sh

238 lines
6.1 KiB
Bash
Raw Normal View History

#!/bin/bash
set -euo pipefail
# TEST-201: Automated install/uninstall test for marketplace apps.
# Runs on the dev server via SSH, testing each app:
# 1. Install via package.install RPC
# 2. Wait for container to start
# 3. Verify health check / port responds
# 4. Uninstall via package.uninstall RPC
# 5. Verify container is removed
#
# Usage: ./scripts/test-app-install.sh [app-id]
# Without args: tests all apps
# With arg: tests only that app
SSH_KEY="${ARCHIPELAGO_SSH_KEY:-$HOME/.ssh/archipelago-deploy}"
TARGET="archipelago@192.168.1.228"
SSH_CMD="ssh -i $SSH_KEY -o StrictHostKeyChecking=no $TARGET"
PASSWORD="password123"
# All marketplace apps and their expected ports
declare -A APP_PORTS=(
[bitcoin-knots]="8332"
[electrs]="50001"
[btcpay-server]="23000"
[lnd]="8080"
[mempool]="18080"
[homeassistant]="8123"
[grafana]="3033"
[searxng]="18888"
[ollama]="11434"
[onlyoffice]="8044"
[penpot]="9001"
[nextcloud]="8085"
[vaultwarden]="8099"
[jellyfin]="8096"
[photoprism]="2342"
[immich]="2283"
[filebrowser]="18082"
[nginx-proxy-manager]="8181"
[portainer]="9443"
[uptime-kuma]="3001"
[tailscale]="0"
[fedimint]="8174"
[indeedhub]="18081"
[dwn]="3000"
[nostr-rs-relay]="18081"
)
# Apps that take a long time or have heavy dependencies — skip in quick mode
HEAVY_APPS="bitcoin-knots electrs btcpay-server immich nextcloud homeassistant"
PASS=0
FAIL=0
SKIP=0
RESULTS=()
log() { echo -e "\033[1;34m[TEST]\033[0m $*"; }
pass() { echo -e "\033[1;32m[PASS]\033[0m $*"; PASS=$((PASS + 1)); RESULTS+=("PASS: $*"); }
fail() { echo -e "\033[1;31m[FAIL]\033[0m $*"; FAIL=$((FAIL + 1)); RESULTS+=("FAIL: $*"); }
skip() { echo -e "\033[1;33m[SKIP]\033[0m $*"; SKIP=$((SKIP + 1)); RESULTS+=("SKIP: $*"); }
# Authenticate and get session cookie
get_session() {
local cookie
cookie=$($SSH_CMD "curl -s -c - http://localhost:5678/rpc/v1 \
-X POST -H 'Content-Type: application/json' \
-d '{\"method\":\"auth.login\",\"params\":{\"password\":\"$PASSWORD\"}}' 2>/dev/null \
| grep session | awk '{print \$NF}'")
echo "$cookie"
}
# Make an authenticated RPC call
rpc_call() {
local session="$1"
local method="$2"
local params="${3:-{}}"
$SSH_CMD "curl -s http://localhost:5678/rpc/v1 \
-X POST -H 'Content-Type: application/json' \
-H 'Cookie: session=$session' \
-d '{\"method\":\"$method\",\"params\":$params}' 2>/dev/null"
}
# Check if a container exists
container_exists() {
local session="$1"
local app_id="$2"
local result
result=$(rpc_call "$session" "container-list")
echo "$result" | grep -q "\"$app_id\"" && return 0 || return 1
}
# Wait for container to appear (up to 60s)
wait_for_container() {
local session="$1"
local app_id="$2"
local max_wait=60
local waited=0
while [ $waited -lt $max_wait ]; do
if container_exists "$session" "$app_id"; then
return 0
fi
sleep 5
waited=$((waited + 5))
done
return 1
}
# Check if port responds
check_port() {
local port="$1"
if [ "$port" = "0" ]; then
return 0 # No port to check (e.g., tailscale)
fi
$SSH_CMD "curl -s -o /dev/null -w '%{http_code}' --connect-timeout 5 http://localhost:$port/ 2>/dev/null" | grep -qE '(200|301|302|401|403|404)' && return 0 || return 1
}
test_app() {
local app_id="$1"
local session="$2"
local port="${APP_PORTS[$app_id]:-0}"
log "Testing $app_id (port: $port)"
# Skip if container already exists (don't disturb running services)
if container_exists "$session" "$app_id"; then
skip "$app_id — already running, skipping to avoid disruption"
return
fi
# 1. Install
log " Installing $app_id..."
local install_result
install_result=$(rpc_call "$session" "package.install" "{\"id\":\"$app_id\"}")
if echo "$install_result" | grep -q '"error"'; then
local err_msg
err_msg=$(echo "$install_result" | grep -o '"message":"[^"]*"' | head -1)
# Dependency errors are expected for some apps
if echo "$err_msg" | grep -qi "dependency\|requires\|must be"; then
skip "$app_id — dependency not met: $err_msg"
return
fi
fail "$app_id — install failed: $err_msg"
return
fi
# 2. Wait for container
log " Waiting for container..."
if ! wait_for_container "$session" "$app_id"; then
fail "$app_id — container did not appear within 60s"
# Try to clean up
rpc_call "$session" "package.uninstall" "{\"id\":\"$app_id\"}" > /dev/null 2>&1
return
fi
# 3. Check port (give it a moment to start)
if [ "$port" != "0" ]; then
sleep 3
log " Checking port $port..."
if check_port "$port"; then
log " Port $port responds"
else
log " Port $port not responding yet (may need more time)"
fi
fi
# 4. Uninstall
log " Uninstalling $app_id..."
local uninstall_result
uninstall_result=$(rpc_call "$session" "package.uninstall" "{\"id\":\"$app_id\"}")
if echo "$uninstall_result" | grep -q '"error"'; then
fail "$app_id — uninstall failed"
return
fi
# 5. Verify container removed
sleep 3
if container_exists "$session" "$app_id"; then
fail "$app_id — container still exists after uninstall"
return
fi
pass "$app_id — install/uninstall cycle complete"
}
main() {
log "=== Archipelago App Install/Uninstall Test ==="
log "Target: $TARGET"
log ""
# Get session
log "Authenticating..."
local session
session=$(get_session)
if [ -z "$session" ]; then
echo "Failed to authenticate. Exiting."
exit 1
fi
log "Session: ${session:0:8}..."
echo ""
# Determine which apps to test
local apps_to_test=()
if [ $# -gt 0 ]; then
apps_to_test=("$@")
else
for app in "${!APP_PORTS[@]}"; do
apps_to_test+=("$app")
done
# Sort for consistent ordering
IFS=$'\n' apps_to_test=($(sort <<<"${apps_to_test[*]}")); unset IFS
fi
log "Testing ${#apps_to_test[@]} apps"
echo ""
for app_id in "${apps_to_test[@]}"; do
test_app "$app_id" "$session"
echo ""
done
# Summary
echo ""
log "=== RESULTS ==="
for r in "${RESULTS[@]}"; do
echo " $r"
done
echo ""
log "Pass: $PASS | Fail: $FAIL | Skip: $SKIP | Total: $((PASS + FAIL + SKIP))"
if [ $FAIL -gt 0 ]; then
exit 1
fi
}
main "$@"