The existing smoke test only checked TCP port connectivity, which cannot
detect a service that accepts connections but returns errors or serves
the wrong content (exactly how the broken Kiwix slipped through).
Add two new validation layers:
1. validate-http.sh — curls every service's health/UI endpoint and
asserts both the HTTP status code AND meaningful body content (JSON
health fields like "database":"ok", XML markers, page strings). Also
verifies Kiwix has a ZIM actually loaded via its OPDS catalog feed.
2. run-browser-tests.sh — drives a real headless Chromium via the
official Playwright Docker image (no host Node install needed) to
confirm pages render correctly after JavaScript execution and SPA
hydration. Updated playwright-services.spec.ts with correct content
tokens and a waitForFunction step that reliably handles SPA timing.
Both are wired into demo-test.sh (new `http` and `browser` subcommands)
and the full test suite. AGENTS.md and demo/AGENTS.md updated with the
new commands and protocols.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
144 lines
5.2 KiB
Bash
Executable File
144 lines
5.2 KiB
Bash
Executable File
#!/bin/bash
|
|
# TSYS Developer Support Stack - Deep HTTP Validation
|
|
# Purpose: post-deployment validation that services actually serve correct
|
|
# HTTP responses (status code + body content), not merely that a TCP
|
|
# port is open. A port being reachable does not prove a service works.
|
|
#
|
|
# Usage: ./scripts/validate-http.sh
|
|
# Exit: 0 if all checks pass, 1 otherwise.
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
ENV_FILE="$PROJECT_ROOT/demo.env"
|
|
|
|
if [[ ! -f "$ENV_FILE" ]]; then
|
|
echo "[ERROR] $ENV_FILE not found. Run ./scripts/demo-stack.sh deploy first." >&2
|
|
exit 1
|
|
fi
|
|
# shellcheck source=/dev/null
|
|
set -a; source "$ENV_FILE"; set +a
|
|
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m'
|
|
|
|
PASS=0
|
|
FAIL=0
|
|
|
|
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
|
log_pass() { echo -e "${GREEN}[PASS]${NC} $1"; PASS=$((PASS + 1)); }
|
|
log_fail() { echo -e "${RED}[FAIL]${NC} $1"; FAIL=$((FAIL + 1)); }
|
|
log_section() { echo -e "\n${BLUE}── $1 ──${NC}"; }
|
|
|
|
# check_http NAME PORT PATH EXPECT_STATUS [CONTENT_SUBSTRING]
|
|
# Follows redirects; HTTP status is the final response code.
|
|
check_http() {
|
|
local name="$1" port="$2" path="$3" expect="$4" content="${5:-}"
|
|
local url="http://localhost:${port}${path}"
|
|
local body status
|
|
body=$(curl -sS -L --max-time 8 "$url" 2>/dev/null) || true
|
|
status=$(curl -sS -L -o /dev/null -w '%{http_code}' --max-time 8 "$url" 2>/dev/null || echo "000")
|
|
|
|
if [[ "$status" == "000" ]]; then
|
|
log_fail "$name ($url) unreachable"
|
|
return
|
|
fi
|
|
if [[ "$status" != "$expect" ]]; then
|
|
log_fail "$name ($url) expected HTTP $expect, got $status"
|
|
return
|
|
fi
|
|
if [[ -n "$content" ]]; then
|
|
if ! echo "$body" | grep -qi "$content"; then
|
|
log_fail "$name ($url) status $status but missing content \"$content\""
|
|
return
|
|
fi
|
|
fi
|
|
log_pass "$name ($url) -> $status${content:+ \"$content\"}"
|
|
}
|
|
|
|
# Specialised check helpers for services whose "healthy" state is best expressed
|
|
# as a JSON field or an XML marker rather than a plain string.
|
|
|
|
check_influxdb() {
|
|
# /health returns {"status":"pass", ...} when ready for queries
|
|
check_http "InfluxDB" "$INFLUXDB_PORT" "/health" "200" '"status":"pass"'
|
|
}
|
|
|
|
check_grafana() {
|
|
# Grafana pretty-prints its JSON, so match with optional whitespace.
|
|
local url="http://localhost:${GRAFANA_PORT}/api/health"
|
|
local body status
|
|
status=$(curl -sS -L -o /dev/null -w '%{http_code}' --max-time 8 "$url" 2>/dev/null || echo "000")
|
|
body=$(curl -sS -L --max-time 8 "$url" 2>/dev/null) || true
|
|
if [[ "$status" == "200" ]] && echo "$body" | grep -qiE '"database"[[:space:]]*:[[:space:]]*"ok"'; then
|
|
log_pass "Grafana ($url) -> 200 \"database\":\"ok\""
|
|
else
|
|
log_fail "Grafana ($url) expected database:ok, got $status"
|
|
fi
|
|
}
|
|
|
|
check_kiwix() {
|
|
# 1. The welcome page must render.
|
|
check_http "Kiwix UI" "$KIWIX_PORT" "/" "200" "welcome to kiwix server"
|
|
# 2. A ZIM must actually be loaded: the OPDS catalog lists at least one entry.
|
|
local url="http://localhost:${KIWIX_PORT}/catalog/v2/entries?count=1"
|
|
local body
|
|
body=$(curl -sS -L --max-time 8 "$url" 2>/dev/null) || true
|
|
if echo "$body" | grep -q "<entry>"; then
|
|
log_pass "Kiwix ZIM loaded (catalog has <entry>)"
|
|
else
|
|
log_fail "Kiwix ($url) no ZIM loaded (catalog feed missing <entry>)"
|
|
fi
|
|
}
|
|
|
|
run_http_validation() {
|
|
log_info "Deep HTTP validation of ${COMPOSE_PROJECT_NAME} services..."
|
|
|
|
log_section "Infrastructure"
|
|
check_http "Homepage" "$HOMEPAGE_PORT" "/" "200"
|
|
check_http "Pi-hole" "$PIHOLE_PORT" "/admin/" "200"
|
|
check_http "Dockhand" "$DOCKHAND_PORT" "/" "200"
|
|
|
|
log_section "Monitoring & Observability"
|
|
check_influxdb
|
|
check_grafana
|
|
check_http "Metrics" "$METRICS_PORT" "/" "200"
|
|
check_http "AppleHealth" "$APPLEHEALTH_PORT" "/health" "200" '"status":"healthy"'
|
|
|
|
log_section "Documentation & Diagramming"
|
|
check_http "Draw.io" "$DRAWIO_PORT" "/" "200"
|
|
check_http "Kroki" "$KROKI_PORT" "/" "200"
|
|
check_kiwix
|
|
|
|
log_section "Developer Tools"
|
|
check_http "AtomicTracker" "$ATOMIC_TRACKER_PORT" "/" "200"
|
|
check_http "ArchiveBox" "$ARCHIVEBOX_PORT" "/health/" "200" "OK"
|
|
check_http "TubeArchivist" "$TUBE_ARCHIVIST_PORT" "/api/health/" "200" "OK"
|
|
check_http "Wakapi" "$WAKAPI_PORT" "/" "200"
|
|
check_http "MailHog" "$MAILHOG_PORT" "/" "200"
|
|
check_http "Atuin" "$ATUIN_PORT" "/healthz" "200" '"status":"healthy"'
|
|
|
|
log_section "Productivity"
|
|
check_http "ReactiveResume" "$REACTIVE_RESUME_PORT" "/api/health" "200"
|
|
check_http "ResumeMatcher" "$RESUME_MATCHER_PORT" "/api/v1/health" "200" '"status":"healthy"'
|
|
|
|
echo ""
|
|
echo "===================================="
|
|
echo "HTTP VALIDATION RESULTS"
|
|
echo "===================================="
|
|
echo -e "Passed: ${GREEN}$PASS${NC}"
|
|
echo -e "Failed: ${RED}$FAIL${NC}"
|
|
if [[ $FAIL -eq 0 ]]; then
|
|
echo -e "\n${GREEN}ALL HTTP CHECKS PASSED${NC}"
|
|
return 0
|
|
else
|
|
echo -e "\n${RED}HTTP VALIDATION FAILED${NC}"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
run_http_validation
|