test: close coverage gaps + opt-in integration smoke
Unit-Tests (kein Docker nötig), 130 Tests gesamt:
- config: ${ENV}- und ~-Expansion in hf_home
- prompt_io: _pin_dns (DNS-Rebinding wird abgewiesen, andere Hosts unberührt,
Resolver wird wiederhergestellt)
- actions: do_check (running/healthy, missing), do_start non-dry-run
(Happy-Path + Readiness-Fehler mit --logs), _container_lock Force-Bypass
- cli: --max-tokens <= 0 abgelehnt, --expose/--no-expose
scripts/smoke.sh: opt-in End-to-End-Test gegen echten Docker + llama.cpp-Server
(--api-key-Round-Trip inkl. 401/200, start/check/chat/stream/stop, eigener
Container/Port, Cleanup-Trap).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
24202feee6
commit
f83f36fdfb
6 changed files with 268 additions and 4 deletions
98
scripts/smoke.sh
Executable file
98
scripts/smoke.sh
Executable file
|
|
@ -0,0 +1,98 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Opt-in Integrations-Rauchtest gegen einen ECHTEN Docker + llama.cpp-Server.
|
||||
# Läuft NICHT in der normalen pytest-Suite (braucht Docker, GPU-Speicher und ein
|
||||
# echtes GGUF-Modell). Startet einen separaten Container (eigener Name/Port), um
|
||||
# eine ggf. laufende Produktion nicht anzufassen — räumt am Ende auf.
|
||||
#
|
||||
# Voraussetzungen:
|
||||
# - Docker-Daemon erreichbar
|
||||
# - freier GPU-Speicher für das in der Config referenzierte Modell
|
||||
# (ein 21-GB-Modell passt NICHT neben einen bereits laufenden 256k-Server;
|
||||
# ggf. SMOKE_GPU auf eine freie Karte setzen oder Produktion vorher stoppen)
|
||||
#
|
||||
# Konfiguration über Environment-Variablen (alle optional):
|
||||
# CLI Pfad zum CLI (Default: ./.venv/bin/llamacppctl)
|
||||
# SMOKE_CONFIG Config-Datei (Default: llama.cpp.config)
|
||||
# SMOKE_PROFILE --profile <name> (Default: keins)
|
||||
# SMOKE_CONTAINER Container-Name (Default: llamacppctl_smoke)
|
||||
# SMOKE_PORT Host-Port (Default: 8099)
|
||||
# SMOKE_GPU GPU-Device (Default: aus Config)
|
||||
# SMOKE_CTX ctx_size für den Test (Default: 8192, klein = schnell/wenig VRAM)
|
||||
# SMOKE_MAX_TOKENS Chat-Budget (Default: 4000; Reasoning-Modelle brauchen viel)
|
||||
# SMOKE_API_KEY API-Key (Default: zufällig)
|
||||
#
|
||||
# Beispiel:
|
||||
# SMOKE_GPU=1 SMOKE_CTX=8192 scripts/smoke.sh
|
||||
#
|
||||
set -uo pipefail
|
||||
|
||||
CLI=${CLI:-./.venv/bin/llamacppctl}
|
||||
CONFIG=${SMOKE_CONFIG:-llama.cpp.config}
|
||||
CONTAINER=${SMOKE_CONTAINER:-llamacppctl_smoke}
|
||||
PORT=${SMOKE_PORT:-8099}
|
||||
CTX=${SMOKE_CTX:-8192}
|
||||
MAX_TOKENS=${SMOKE_MAX_TOKENS:-4000}
|
||||
API_KEY=${SMOKE_API_KEY:-smoke-$$-$RANDOM}
|
||||
|
||||
base=(--config "$CONFIG" --container-name "$CONTAINER" --host-port "$PORT")
|
||||
[ -n "${SMOKE_PROFILE:-}" ] && base+=(--profile "$SMOKE_PROFILE")
|
||||
[ -n "${SMOKE_GPU:-}" ] && base+=(--gpu "$SMOKE_GPU")
|
||||
|
||||
pass=0
|
||||
fail=0
|
||||
ok() { echo " PASS: $1"; pass=$((pass + 1)); }
|
||||
bad() { echo " FAIL: $1"; fail=$((fail + 1)); }
|
||||
|
||||
cleanup() {
|
||||
echo "== cleanup =="
|
||||
"$CLI" --stop "${base[@]}" >/dev/null 2>&1 || docker rm -f "$CONTAINER" >/dev/null 2>&1 || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
command -v docker >/dev/null 2>&1 || { echo "docker fehlt auf PATH"; exit 2; }
|
||||
docker version >/dev/null 2>&1 || { echo "docker-Daemon nicht erreichbar"; exit 2; }
|
||||
|
||||
echo "== dry-run =="
|
||||
if "$CLI" --start "${base[@]}" --api-key "$API_KEY" --ctx-size "$CTX" --dry-run | grep -q "docker run"; then
|
||||
ok "dry-run zeigt docker-run-Kommando (kein Start)"
|
||||
else
|
||||
bad "dry-run"
|
||||
fi
|
||||
|
||||
echo "== start (api-key gesetzt, ctx=$CTX) =="
|
||||
if "$CLI" --start "${base[@]}" --api-key "$API_KEY" --ctx-size "$CTX"; then
|
||||
ok "start + readiness"
|
||||
else
|
||||
bad "start (Modell/GPU prüfen)"; exit 1
|
||||
fi
|
||||
|
||||
echo "== --check Exit-Code (läuft -> 0) =="
|
||||
rc=0; "$CLI" --check "${base[@]}" >/dev/null 2>&1 || rc=$?
|
||||
[ "$rc" -eq 0 ] && ok "--check == 0" || bad "--check == $rc (erwartet 0)"
|
||||
|
||||
echo "== API-Key erzwungen: ohne Key -> 401 =="
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:$PORT/v1/models")
|
||||
[ "$code" = "401" ] && ok "unauth -> 401" || bad "unauth -> $code (erwartet 401)"
|
||||
|
||||
echo "== API-Key: mit Key -> 200 =="
|
||||
code=$(curl -s -o /dev/null -w '%{http_code}' -H "Authorization: Bearer $API_KEY" "http://127.0.0.1:$PORT/v1/models")
|
||||
[ "$code" = "200" ] && ok "auth -> 200" || bad "auth -> $code (erwartet 200)"
|
||||
|
||||
echo "== chat (Tool sendet Bearer-Key) =="
|
||||
out=$("$CLI" --chat "${base[@]}" --api-key "$API_KEY" --max-tokens "$MAX_TOKENS" -p "Sag in genau einem Satz Hallo." 2>/dev/null)
|
||||
[ -n "$out" ] && ok "chat liefert Text: ${out:0:60}..." || bad "chat leer"
|
||||
|
||||
echo "== stream =="
|
||||
sout=$("$CLI" --chat "${base[@]}" --api-key "$API_KEY" --stream --max-tokens "$MAX_TOKENS" -p "Zähle bis drei." 2>/dev/null)
|
||||
[ -n "$sout" ] && ok "stream liefert Text" || bad "stream leer"
|
||||
|
||||
echo "== stop -> --check == 5 =="
|
||||
rc=0; "$CLI" --stop "${base[@]}" >/dev/null 2>&1 || rc=$?
|
||||
[ "$rc" -eq 0 ] && ok "stop" || bad "stop == $rc"
|
||||
rc=0; "$CLI" --check "${base[@]}" >/dev/null 2>&1 || rc=$?
|
||||
[ "$rc" -eq 5 ] && ok "--check nach stop == 5" || bad "--check nach stop == $rc (erwartet 5)"
|
||||
|
||||
echo
|
||||
echo "== Ergebnis: $pass PASS, $fail FAIL =="
|
||||
[ "$fail" -eq 0 ]
|
||||
Loading…
Add table
Add a link
Reference in a new issue