Files
AI-Profile-Router/smoke-test.sh
T

111 lines
4.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# Short, read-only end-to-end test for every Athena release.
set -Eeuo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE=${STACK_ENV:-/etc/mike-ai/stack.env}
pass() { printf 'PASS %s\n' "$*"; }
fail() { printf 'FAIL %s\n' "$*" >&2; exit 1; }
[[ $EUID -eq 0 ]] || fail "Bitte als root ausfuehren."
command -v docker >/dev/null || fail "Docker fehlt."
[[ -s $ENV_FILE ]] || fail "Stack-Konfiguration fehlt: $ENV_FILE"
cd "$ROOT_DIR"
./manage.sh validate >/dev/null
pass "Profilmatrix und Compose-Konfiguration stimmen"
healthy() {
local name=$1 state health
state=$(docker inspect -f '{{.State.Status}}' "$name" 2>/dev/null || true)
health=$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{end}}' "$name" 2>/dev/null || true)
[[ $state == running && ( -z $health || $health == healthy ) ]]
}
for name in \
mike-ai-wireguard-gateway \
mike-ai-profile-controller \
mike-ai-embedding \
mike-ai-router \
mike-ai-qwen3-tts \
mike-ai-tts-gateway \
mike-ai-llama-dashboard \
mike-ai-mcp-athena-operator \
mike-ai-backup; do
healthy "$name" || fail "$name fehlt oder ist nicht gesund"
done
pass "Athena-Kerndienste sind gesund"
docker exec mike-ai-embedding curl -fsS \
-H 'Content-Type: application/json' \
-d '{"model":"embeddinggemma","input":["Athena Speicherprobe"]}' \
http://127.0.0.1:8082/v1/embeddings | \
python3 -c 'import json,sys; data=json.load(sys.stdin); assert len(data["data"][0]["embedding"]) == 768' || \
fail "CPU-Embedding-Endpunkt liefert keinen 768-dimensionalen Vektor"
pass "CPU-Embedding-Endpunkt funktioniert"
for name in mike-ai-llama-dashboard mike-ai-portainer; do
network_mode=$(docker inspect -f '{{.HostConfig.NetworkMode}}' "$name" 2>/dev/null || true)
[[ $network_mode != container:* ]] || \
fail "$name teilt noch einen fluechtigen Container-Netzwerk-Namespace"
done
pass "Dashboard und Portainer besitzen stabile Netzwerk-Namespaces"
active_llama=$(docker ps --format '{{.Names}}' | \
grep -Ec '^mike-ai-llama-(fast|medium|large|ultra|uncensored)$' || true)
[[ $active_llama -eq 1 ]] || fail "$active_llama aktive llama-Profile (erwartet: 1)"
pass "Genau ein llama.cpp-Profil ist aktiv"
for legacy in \
mike-ai-open-webui \
mike-ai-hermes \
mike-ai-hermes-webui \
mike-ai-hermes-webui-vpn-proxy \
mike-ai-mcp-web \
mike-ai-tools-searxng \
mike-ai-tools-tinysearch \
mike-ai-mcp-platform-context \
mike-ai-mcp-arr \
mike-ai-mcp-deemix \
mike-ai-mcp-github \
mike-ai-mcp-homeassistant \
mike-ai-mcp-navidrome; do
[[ -z $(docker inspect -f '{{.Name}}' "$legacy" 2>/dev/null || true) ]] || \
fail "Altlast existiert noch: $legacy"
done
pass "OpenWebUI, alte Hermes-Dienste und migrierte MCPs sind entfernt"
docker exec -i mike-ai-router python - <<'PY' >/dev/null
import json
import os
import urllib.request
key = os.environ["ROUTER_API_KEY"]
for path in ("/health", "/v1/models", "/status"):
request = urllib.request.Request(
"http://127.0.0.1:8081" + path,
headers={"Authorization": "Bearer " + key},
)
with urllib.request.urlopen(request, timeout=10) as response:
body = response.read()
if response.status != 200:
raise SystemExit(f"{path}: HTTP {response.status}")
data = json.loads(body)
if path.endswith("models") and not data.get("data"):
raise SystemExit("Router meldet keine Modelle")
if path == "/status":
if data.get("current_profile") not in data.get("profiles", {}):
raise SystemExit("Router erkennt das aktive Profil nicht")
if not data.get("qwen", {}).get("available"):
raise SystemExit("Router meldet Qwen als nicht verfügbar")
PY
pass "Router erkennt aktives Profil; Liveness und Modellliste funktionieren"
latest=/data/docker-backups/athena-latest.tar.gz
[[ -s $latest ]] || fail "Kein gueltiges Athena-Backup: $latest"
./restore.sh --check "$latest" >/dev/null || fail "Athena-Backup ist nicht wiederherstellbar"
pass "Automatisches Athena-Backup ist vorhanden und strukturell wiederherstellbar"
printf '\nATHENA_E2E_OK\n'