- web/: token-login, sessionslista, ANSI-skärmvy (SSE+poll), promptfält, frågekort, tangentrad, delningsflik (bild/HTML-sandbox/markdown), inställningar - go:embed via webfs.go — servern förblir en enda binär, strikt CSP - browser-verifierad mot riktig agy (fråga -> kort -> svar -> kört) - Dockerfile (alpine + tmux/bash) + helmd-image.yaml -> localhost:5000/helmd - integrationstest utökat till 29 gröna Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
133 lines
6.2 KiB
Bash
Executable File
133 lines
6.2 KiB
Bash
Executable File
#!/bin/bash
|
|
# Integrationstest för helmd. Körs i en isolerad container (se
|
|
# scripts/run-integration.sh) men fungerar var som helst där tmux,
|
|
# curl och binären finns och där HOME får smutsas ner.
|
|
set -eu
|
|
|
|
BIN="${HELMD_BIN:-./build/helmd}"
|
|
export HOME="${TEST_HOME:-$(mktemp -d)}"
|
|
export HELMD_CONFIG="$HOME/helmd-config.json"
|
|
API=http://127.0.0.1:8788
|
|
PASS=0; FAIL=0
|
|
|
|
say() { printf '\n== %s\n' "$*"; }
|
|
ok() { PASS=$((PASS+1)); echo " OK: $*"; }
|
|
fail() { FAIL=$((FAIL+1)); echo " FAIL: $*" >&2; }
|
|
|
|
expect_contains() { # haystackfil, nål, beskrivning
|
|
if grep -qF "$2" "$1"; then ok "$3"; else fail "$3 — hittade inte: $2"; cat "$1" | tail -20; fi
|
|
}
|
|
|
|
curl_api() { curl -sS -H "Authorization: Bearer $TOKEN" "$@"; }
|
|
|
|
wait_for() { # beskrivning, timeout-sek, kommando...
|
|
local desc="$1" t="$2"; shift 2
|
|
for _ in $(seq 1 $((t*2))); do
|
|
if "$@" >/dev/null 2>&1; then ok "$desc"; return 0; fi
|
|
sleep 0.5
|
|
done
|
|
fail "$desc (timeout ${t}s)"; return 1
|
|
}
|
|
|
|
say "startar helmd"
|
|
TOKEN=$("$BIN" token)
|
|
"$BIN" serve --listen 127.0.0.1:8788 &
|
|
HELMD_PID=$!
|
|
trap 'kill $HELMD_PID 2>/dev/null || true; tmux kill-server 2>/dev/null || true' EXIT
|
|
wait_for "servern svarar på /api/health" 10 \
|
|
curl -fsS -H "Authorization: Bearer $TOKEN" $API/api/health
|
|
|
|
say "auth"
|
|
CODE=$(curl -s -o /dev/null -w '%{http_code}' $API/api/sessions)
|
|
[ "$CODE" = 401 ] && ok "utan token => 401" || fail "utan token gav $CODE"
|
|
CODE=$(curl -s -o /dev/null -w '%{http_code}' "$API/api/sessions?token=$TOKEN")
|
|
[ "$CODE" = 200 ] && ok "query-token => 200" || fail "query-token gav $CODE"
|
|
|
|
say "webbklient (inbäddad)"
|
|
curl -s $API/ > /tmp/ui.html
|
|
expect_contains /tmp/ui.html '<title>agent-helm</title>' "index.html serveras utan auth"
|
|
curl -s $API/app.js | grep -q 'ansiToHtml' && ok "app.js serveras" || fail "app.js"
|
|
CODE=$(curl -s -o /dev/null -w '%{http_code}' $API/style.css)
|
|
[ "$CODE" = 200 ] && ok "style.css => 200" || fail "style.css gav $CODE"
|
|
|
|
say "stäng av ntfy för testet (PUT /api/config)"
|
|
curl_api -X PUT -d '{"ntfy":{"server":"https://ntfy.brasse-pc.eu","topic":"agent-helm","allowed_topics":["agent-helm","claude"],"enabled":false,"notify_on_question":false}}' \
|
|
$API/api/config > /tmp/cfg.json
|
|
expect_contains /tmp/cfg.json '"saved":true' "config sparad"
|
|
curl_api $API/api/config | grep -q '"enabled":false' && ok "ntfy avstängd" || fail "ntfy fortfarande på"
|
|
|
|
say "session: skapa med mock-agent"
|
|
MOCK="$(cd "$(dirname "$0")/.." && pwd)/testdata/mock-agent.sh"
|
|
curl_api -X POST -d "{\"name\":\"prov\",\"cmd\":\"bash $MOCK\",\"cwd\":\"/tmp\"}" \
|
|
$API/api/sessions > /tmp/sess.json
|
|
expect_contains /tmp/sess.json '"name":"prov"' "session skapad"
|
|
|
|
say "fråga 1: onumrerad trust-fråga (pilnavigering)"
|
|
q_has_two_options() { curl_api $API/api/sessions/prov/question | grep -q '"No, exit"'; }
|
|
wait_for "trust-frågan detekterad" 10 q_has_two_options
|
|
curl_api $API/api/sessions/prov/question > /tmp/q1.json
|
|
expect_contains /tmp/q1.json '"selected":0' "markören står på första valet"
|
|
curl_api -X POST -d '{"option":2}' $API/api/sessions/prov/answer > /tmp/a1.json
|
|
expect_contains /tmp/a1.json '"answered":true' "svar skickat"
|
|
screen_has() { curl_api "$API/api/sessions/prov/screen" | grep -qF "$1"; }
|
|
wait_for "mocken fick svaret (ned+enter => val 1)" 10 screen_has "TRUST_ANSWER=1"
|
|
|
|
say "prompt"
|
|
curl_api -X POST -d '{"text":"hej från testet"}' $API/api/sessions/prov/prompt > /dev/null
|
|
wait_for "prompten nådde agenten" 10 screen_has "ECHO: hej från testet"
|
|
|
|
say "fråga 2: numrerad godkännandefråga (sifferval)"
|
|
curl_api -X POST -d '{"text":"ask-numbered"}' $API/api/sessions/prov/prompt > /dev/null
|
|
q2_detected() { curl_api $API/api/sessions/prov/question | grep -q '"numbered":true'; }
|
|
wait_for "numrerad fråga detekterad" 10 q2_detected
|
|
curl_api -X POST -d '{"option":3}' $API/api/sessions/prov/answer > /dev/null
|
|
wait_for "mocken fick siffran 3" 10 screen_has "PICKED=3"
|
|
|
|
say "raw keys + mode"
|
|
curl_api -X POST -d '{"keys":["Enter"]}' $API/api/sessions/prov/keys | grep -q '"sent":true' \
|
|
&& ok "keys-endpoint" || fail "keys-endpoint"
|
|
curl_api -X POST -d '{"action":"cycle"}' $API/api/sessions/prov/mode | grep -q '"sent":true' \
|
|
&& ok "mode cycle (BTab)" || fail "mode cycle"
|
|
|
|
say "SSE-events"
|
|
(curl_api -N --max-time 4 "$API/api/events" > /tmp/events.txt 2>/dev/null || true) &
|
|
SSE_PID=$!
|
|
sleep 0.5
|
|
curl_api -X POST -d '{"text":"sse-test"}' $API/api/sessions/prov/prompt > /dev/null
|
|
wait $SSE_PID || true
|
|
expect_contains /tmp/events.txt 'event: hello' "SSE hello"
|
|
expect_contains /tmp/events.txt 'event: screen' "SSE screen-event vid ändring"
|
|
|
|
say "fildelning"
|
|
printf '<h1>rapport</h1>' > /tmp/rapport.html
|
|
"$BIN" share --note "testrapport" --session prov /tmp/rapport.html > /tmp/share.out
|
|
expect_contains /tmp/share.out "delad: rapport.html" "helmd share-CLI"
|
|
curl_api $API/api/shares > /tmp/shares.json
|
|
expect_contains /tmp/shares.json '"note":"testrapport"' "delningen listas"
|
|
SHARE_ID=$(grep -o '"id":"[a-f0-9]*"' /tmp/shares.json | head -1 | cut -d'"' -f4)
|
|
curl_api "$API/api/shares/$SHARE_ID/raw" > /tmp/share.raw
|
|
expect_contains /tmp/share.raw '<h1>rapport</h1>' "raw-innehåll serveras"
|
|
printf 'x' > /tmp/otillaten.sh
|
|
CODE=$(curl_api -s -o /dev/null -w '%{http_code}' -F file=@/tmp/otillaten.sh $API/api/shares)
|
|
[ "$CODE" = 400 ] && ok "otillåten filtyp => 400" || fail "otillåten filtyp gav $CODE"
|
|
curl_api -X DELETE "$API/api/shares/$SHARE_ID" | grep -q '"deleted":true' \
|
|
&& ok "delning raderad" || fail "radering"
|
|
|
|
say "notify-regler"
|
|
CODE=$(curl_api -s -o /dev/null -w '%{http_code}' -X POST -d '{"topic":"hemligt","message":"x"}' $API/api/notify)
|
|
[ "$CODE" = 403 ] && ok "otillåten topic => 403" || fail "otillåten topic gav $CODE"
|
|
curl_api -X POST -d '{"topic":"claude","message":"tyst (ntfy disabled)"}' $API/api/notify \
|
|
| grep -q '"published":true' && ok "tillåten topic => ok (ntfy av => no-op)" || fail "tillåten topic"
|
|
|
|
say "session: avsluta + döda"
|
|
curl_api -X POST -d '{"text":"exit"}' $API/api/sessions/prov/prompt > /dev/null
|
|
sess_dead() { curl_api $API/api/sessions/prov | grep -q '"alive":false'; }
|
|
wait_for "exit upptäcks (alive=false)" 10 sess_dead
|
|
curl_api -X DELETE $API/api/sessions/prov | grep -q '"killed":true' \
|
|
&& ok "session dödad" || fail "kill"
|
|
|
|
echo
|
|
echo "================================"
|
|
echo "PASS: $PASS FAIL: $FAIL"
|
|
[ "$FAIL" = 0 ] && echo "ALLA TESTER GRÖNA" || exit 1
|