#!/usr/bin/env bash # Re-record ground-truth captures into tests/fixtures/. # # ./scripts/capture-fixtures.sh # needs the tunnels up: scripts/dev-tunnels.sh start # ./scripts/capture-fixtures.sh --with-llm # also scrape the live engines' /metrics # # Fixtures are verbatim, not hand-written: a fixture you typed is a test of your memory. # When a host's kernel or an engine's metric names change, re-run this and read the diff. set -uo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" FIX="$ROOT/tests/fixtures" mkdir -p "$FIX" HOSTS=(driveripper:9121 deskwork:9122 gateway:9123) # Engine URLs come from the same env the app uses, so this script carries no deployment # detail of its own — and never records a tokenised URL into a fixture by accident. if [ -f "$ROOT/.env.dev" ]; then set -a; . "$ROOT/.env.dev"; set +a fi ENGINES=() [ -n "${GUFO_BASE_URL:-}" ] && ENGINES+=("gufo:${GUFO_BASE_URL%/}/metrics") [ -n "${VLLM_BASE_URL:-}" ] && ENGINES+=("vllm:${VLLM_BASE_URL%/}/metrics") failed=0 for entry in "${HOSTS[@]}"; do name=${entry%%:*}; port=${entry##*:} url="http://127.0.0.1:${port}/snapshot" if curl -fsS --max-time 8 "$url" -o "$FIX/agent_${name}.json"; then echo " ✓ agent_${name}.json ($(wc -c <"$FIX/agent_${name}.json") bytes)" else echo " ✗ agent_${name}.json — no agent on $url (scripts/dev-tunnels.sh start?)" failed=1 fi done if [ "${1:-}" = "--with-llm" ]; then if [ ${#ENGINES[@]} -eq 0 ]; then echo " ! no GUFO_BASE_URL / VLLM_BASE_URL set (see .env.dev) — nothing to capture" failed=1 fi for entry in "${ENGINES[@]}"; do name=${entry%%:*}; url=${entry#*:} if curl -fsS --max-time 20 "$url" -o "$FIX/metrics_${name}.txt"; then echo " ✓ metrics_${name}.txt ($(wc -l <"$FIX/metrics_${name}.txt") lines)" else echo " ✗ metrics_${name}.txt — $url unreachable (VPN? engine down?)" failed=1 fi done else echo " (skipped engine /metrics captures — pass --with-llm when the engines are reachable)" fi # Secrets never belong in a fixture: engines can expose /metrics behind a token in the URL, # and a hostname you use at home is a hostname in a public repo. if grep -rlE "Authorization|Bearer |://[^/]*:[^/@]*@" "$FIX" >/dev/null 2>&1; then echo " ! fixture contains what looks like a credential — scrub it before committing" failed=1 fi # Two things leak out of a real capture that nobody notices: LUKS/partition UUIDs (which # identify *your* disks, and appear in `device` strings as /dev/mapper/luks-) and the # FQDN a host thinks it has. They are replaced deterministically so re-capturing does not # churn the fixture, and the tests keep the same shapes to assert against. python3 - "$FIX" <<'PY' import pathlib import re import sys uuid_re = re.compile(r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}") SYNTHETIC = "00000000-0000-4000-8000-" # already-scrubbed markers: re-running must not churn fix = pathlib.Path(sys.argv[1]) changed = [] for path in sorted(fix.iterdir()): if path.suffix not in (".json", ".txt"): continue text = path.read_text() seen: dict[str, str] = {} def swap(match: re.Match[str]) -> str: value = match.group(0) if value.startswith(SYNTHETIC): return value if value not in seen: seen[value] = f"00000000-0000-4000-8000-{len(seen) + 1:012d}" return seen[value] text = uuid_re.sub(swap, text) # "hostname": "gw.lab.example.com" -> "gw": the dashboard's host key is the target name # from HOST_TARGETS, never whatever the box calls itself text = re.sub(r'("hostname": "[^"]*?)\.[^"]*(")', r"\1\2", text) if text != path.read_text(): path.write_text(text) changed.append(path.name) print(f" scrubbed identifiers in: {', '.join(changed)}" if changed else " fixtures carry no identifiers") PY exit "$failed"