#!/usr/bin/env bash set -euo pipefail API_URL="${API_URL:-http://127.0.0.1:8080}" TMP_DIR="$(mktemp -d)" trap 'rm -rf "$TMP_DIR"' EXIT check_json_get() { local path="$1" local keys_csv="$2" local out_file="$TMP_DIR/out.json" local code code="$(curl -sS --max-time 15 -o "$out_file" -w "%{http_code}" "${API_URL}${path}")" if [[ "$code" != "200" ]]; then echo "[sanity] ${path} -> HTTP ${code}" >&2 cat "$out_file" >&2 || true return 1 fi python3 - "$out_file" "$keys_csv" "$path" <<'PY' import json import sys file_path, keys_csv, path = sys.argv[1], sys.argv[2], sys.argv[3] keys = [k for k in keys_csv.split(",") if k] with open(file_path, "r", encoding="utf-8") as f: data = json.load(f) for key in keys: if key not in data: print(f"[sanity] {path}: missing key '{key}'", file=sys.stderr) sys.exit(1) print(f"[sanity] {path}: OK") PY } echo "[sanity] API_URL=${API_URL}" check_json_get "/healthz" "status,time" check_json_get "/api/v1/status" "timestamp,iface,table" check_json_get "/api/v1/routes/status" "timestamp,iface,table" check_json_get "/api/v1/traffic/mode" "mode,healthy,message" check_json_get "/api/v1/traffic/interfaces" "interfaces,preferred_iface,active_iface" check_json_get "/api/v1/dns/status" "mode,smartdns_addr,via_smartdns" check_json_get "/api/v1/vpn/status" "status_word,unit_state" check_json_get "/api/v1/vpn/autoloop-status" "status_word,raw_text" check_json_get "/api/v1/routes/timer" "enabled" # Method check: POST-only endpoint should reject GET. code="$(curl -sS --max-time 10 -o /dev/null -w "%{http_code}" "${API_URL}/api/v1/routes/update")" if [[ "$code" != "405" ]]; then echo "[sanity] /api/v1/routes/update GET expected 405, got ${code}" >&2 exit 1 fi echo "[sanity] method guard OK (/api/v1/routes/update GET => 405)" # Quick stream availability: open SSE briefly; timeout is expected. headers_file="$TMP_DIR/events.headers" set +e http_code="$(curl -sS --max-time 2 -D "$headers_file" -o /dev/null -w "%{http_code}" "${API_URL}/api/v1/events/stream")" curl_rc=$? set -e if [[ "$http_code" != "200" ]]; then echo "[sanity] /api/v1/events/stream -> HTTP ${http_code}" >&2 cat "$headers_file" >&2 || true exit 1 fi if [[ $curl_rc -ne 0 && $curl_rc -ne 28 ]]; then echo "[sanity] unexpected curl rc=${curl_rc} for events stream" >&2 exit 1 fi if ! grep -qi '^Content-Type: text/event-stream' "$headers_file"; then echo "[sanity] /api/v1/events/stream missing text/event-stream content-type" >&2 cat "$headers_file" >&2 || true exit 1 fi echo "[sanity] events stream headers OK (curl rc=${curl_rc})" echo "[sanity] all checks passed"