diff --git a/tests/dashboard.sh b/tests/dashboard.sh new file mode 100755 index 0000000..c268375 --- /dev/null +++ b/tests/dashboard.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env bash +# Phase 6 + 7 smoke test: dashboard HTTP, per-container web UIs, +# approve-by-POST, SSE endpoint, and orphan-approval GC. +# +# Runs as root on a host with services.hive-c0re enabled and the hm1nd +# container declared. Idempotent — wipes any prior alice state. + +set -euo pipefail + +AGENT=alice +PKG=htop +HOST=${HOST:-localhost} +DASH_PORT=${DASH_PORT:-7000} +MGR_PORT=${MGR_PORT:-8000} + +cleanup() { + sudo hive-c0re kill "$AGENT" 2>/dev/null || true + sudo nixos-container destroy "h-${AGENT}" 2>/dev/null || true + sudo rm -rf \ + "/var/lib/hyperhive/agents/${AGENT}" \ + "/var/lib/hyperhive/applied/${AGENT}" +} + +echo "=== precheck: hm1nd container is up ===" +if ! sudo machinectl status hm1nd >/dev/null 2>&1; then + echo " starting via systemd..." + sudo systemctl start container@hm1nd.service + for i in 1 2 3 4 5 6 7 8 9 10; do + sudo machinectl status hm1nd >/dev/null 2>&1 && break + sleep 0.5 + done +fi +echo " ✓ hm1nd is up" + +cleanup + +echo "=== dashboard reachable ===" +curl -sf "http://${HOST}:${DASH_PORT}/" | grep -q "HYPERHIVE" || { + echo "FAIL: dashboard not serving expected content" + exit 1 +} +echo " ✓ http://${HOST}:${DASH_PORT}/ → HTML with HYPERHIVE banner" + +echo "=== /messages/stream SSE endpoint ===" +curl -sI "http://${HOST}:${DASH_PORT}/messages/stream" \ + | grep -qi "content-type: text/event-stream" || { + echo "FAIL: /messages/stream is not SSE" + exit 1 +} +echo " ✓ Content-Type: text/event-stream" + +echo "=== manager UI reachable ===" +curl -sf "http://${HOST}:${MGR_PORT}/" | grep -q "hm1nd" || { + echo "FAIL: manager UI not reachable at :${MGR_PORT}" + exit 1 +} +echo " ✓ http://${HOST}:${MGR_PORT}/ → hm1nd label" + +echo "=== spawn ${AGENT} ===" +sudo hive-c0re spawn "$AGENT" + +echo "=== pick up ${AGENT}'s port from dashboard ===" +sleep 2 +ALICE_PORT=$(curl -sf "http://${HOST}:${DASH_PORT}/" \ + | sed -nE "s|.*href=\"http://[^:]+:([0-9]+)/\">${AGENT}<.*|\\1|p" \ + | head -1) +if [ -z "$ALICE_PORT" ]; then + echo "FAIL: ${AGENT} port not found in dashboard" + exit 1 +fi +echo " ✓ ${AGENT} port: ${ALICE_PORT}" + +echo "=== ${AGENT} UI reachable ===" +for i in 1 2 3 4 5 6 7 8 9 10; do + curl -sf "http://${HOST}:${ALICE_PORT}/" | grep -q "${AGENT}" && break + sleep 1 +done +curl -sf "http://${HOST}:${ALICE_PORT}/" | grep -q "${AGENT}" || { + echo "FAIL: ${AGENT} UI not reachable at :${ALICE_PORT}" + exit 1 +} +echo " ✓ http://${HOST}:${ALICE_PORT}/ → ${AGENT} label" + +echo "=== ${PKG} not in path pre-approve ===" +if sudo nixos-container run "h-${AGENT}" -- which "$PKG" 2>/dev/null; then + echo "FAIL: ${PKG} already in path" + exit 1 +fi +echo " ✓" + +echo "=== manager submits approval (via hm1nd) ===" +sudo nixos-container run hm1nd -- bash -c " + set -euo pipefail + cd /agents/${AGENT}/config + cat > agent.nix <<'EOF' +{ pkgs, ... }: +{ + environment.systemPackages = [ pkgs.${PKG} ]; +} +EOF + git commit -am 'add ${PKG}' + SHA=\$(git rev-parse HEAD) + echo \" manager commit SHA=\$SHA\" + hive-m1nd request-apply-commit ${AGENT} \$SHA +" + +echo "=== pick approval id from dashboard ===" +ID=$(curl -sf "http://${HOST}:${DASH_PORT}/" \ + | sed -nE 's|.*hive-c0re approve ([0-9]+).*|\1|p' \ + | head -1) +if [ -z "$ID" ]; then + echo "FAIL: approval id not found in dashboard" + exit 1 +fi +echo " ✓ approval id: ${ID}" + +echo "=== POST /approve/${ID} (browser button equivalent) ===" +HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}' \ + -X POST "http://${HOST}:${DASH_PORT}/approve/${ID}") +if [ "$HTTP_CODE" != "303" ] && [ "$HTTP_CODE" != "302" ]; then + echo "FAIL: expected 303 redirect, got HTTP ${HTTP_CODE}" + exit 1 +fi +echo " ✓ HTTP ${HTTP_CODE} (redirect)" + +echo "=== verify ${PKG} now in h-${AGENT} ===" +sudo nixos-container run "h-${AGENT}" -- which "$PKG" + +echo "=== orphan-GC: cleanup ${AGENT}, refresh dashboard, orphan disappears ===" +sudo nixos-container run hm1nd -- bash -c " + set -euo pipefail + cd /agents/${AGENT}/config + echo '{ ... }: { }' > agent.nix + git commit -am 'noop' + SHA=\$(git rev-parse HEAD) + hive-m1nd request-apply-commit ${AGENT} \$SHA +" +ORPHAN_ID=$(sudo hive-c0re pending \ + | sed -nE 's/^[[:space:]]*"id":[[:space:]]*([0-9]+).*/\1/p' \ + | tail -1) +echo " queued orphan id: ${ORPHAN_ID}" +cleanup +# First dashboard render triggers GC. +curl -sf "http://${HOST}:${DASH_PORT}/" >/dev/null +sleep 0.5 +if sudo hive-c0re pending | grep -q "\"id\": ${ORPHAN_ID}"; then + echo "FAIL: orphan ${ORPHAN_ID} still pending after dashboard render" + exit 1 +fi +echo " ✓ orphan ${ORPHAN_ID} cleaned up" + +echo +echo "=== summary ===" +echo " dashboard http://${HOST}:${DASH_PORT}/" +echo " manager UI http://${HOST}:${MGR_PORT}/" +echo " (alice torn down)"