Files
TSYSDevStack-SupportStack-L…/demo/scripts/validate-all.sh
T
mrcharles f0101e482b feat(demo): add deep HTTP and browser validation layers
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
2026-07-30 15:35:34 -05:00

269 lines
9.0 KiB
Bash
Executable File

#!/bin/bash
# TSYS Developer Support Stack - Comprehensive Validation Script
# Purpose: Proactive issue prevention before deployment
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
DEMO_DIR="$PROJECT_ROOT"
VALIDATION_PASSED=0
VALIDATION_FAILED=0
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m'
log_validation() { echo -e "${BLUE}[VALIDATE]${NC} $1"; }
log_pass() { echo -e "${GREEN}[PASS]${NC} $1"; VALIDATION_PASSED=$((VALIDATION_PASSED + 1)); }
log_fail() { echo -e "${RED}[FAIL]${NC} $1"; VALIDATION_FAILED=$((VALIDATION_FAILED + 1)); }
validate_yaml_files() {
log_validation "Validating YAML files with yamllint..."
local yaml_files=(
"docker-compose.yml.template"
"config/homepage/docker.yaml"
"config/grafana/datasources.yml"
"config/grafana/dashboards.yml"
)
for yaml_file in "${yaml_files[@]}"; do
if [[ -f "$DEMO_DIR/$yaml_file" ]]; then
if docker run --rm -v "$DEMO_DIR:/data" cytopia/yamllint -c /data/.yamllint /data/"$yaml_file" 2>&1; then
log_pass "YAML validation: $yaml_file"
else
log_fail "YAML validation: $yaml_file"
fi
else
log_fail "YAML file not found: $yaml_file"
fi
done
}
validate_shell_scripts() {
log_validation "Validating shell scripts with shellcheck..."
local shell_files=(
"scripts/demo-stack.sh"
"scripts/demo-test.sh"
"scripts/validate-all.sh"
"scripts/validate-http.sh"
"tests/unit/test_env_validation.sh"
"tests/integration/test_service_communication.sh"
"tests/e2e/test_deployment_workflow.sh"
"tests/e2e/run-browser-tests.sh"
)
for shell_file in "${shell_files[@]}"; do
if [[ -f "$DEMO_DIR/$shell_file" ]]; then
if docker run --rm -v "$DEMO_DIR:/data" koalaman/shellcheck /data/"$shell_file" 2>&1; then
log_pass "Shell validation: $shell_file"
else
log_fail "Shell validation: $shell_file"
fi
else
log_fail "Shell file not found: $shell_file"
fi
done
}
validate_docker_images() {
log_validation "Validating Docker image availability..."
local images=(
"tecnativa/docker-socket-proxy:latest"
"ghcr.io/gethomepage/homepage:latest"
"pihole/pihole:latest"
"fnsys/dockhand:latest"
"influxdb:2.7-alpine"
"grafana/grafana:latest"
"fjudith/draw.io:latest"
"yuzutech/kroki:latest"
"ghcr.io/majorpeter/atomic-tracker:v1.3.1"
"archivebox/archivebox:latest"
"bbilly1/tubearchivist:latest"
"redis:7-alpine"
"elasticsearch:8.12.0"
"ghcr.io/muety/wakapi:latest"
"mailhog/mailhog:latest"
"ghcr.io/atuinsh/atuin:v18.10.0"
"amruthpillai/reactive-resume:latest"
"postgres:16-alpine"
"chrislusf/seaweedfs:latest"
"quay.io/minio/mc:latest"
"ghcr.io/lowlighter/metrics:latest"
"ghcr.io/kiwix/kiwix-serve:latest"
"ghcr.io/srbhr/resume-matcher:latest"
)
for image in "${images[@]}"; do
if docker image inspect "$image" >/dev/null 2>&1; then
log_pass "Docker image available: $image"
else
log_fail "Docker image not available: $image"
fi
done
}
validate_port_availability() {
log_validation "Validating port availability..."
set -a; source "$DEMO_DIR/demo.env" 2>/dev/null || source "$DEMO_DIR/demo.env.template" 2>/dev/null || true; set +a
local ports=(
"$HOMEPAGE_PORT"
"$PIHOLE_PORT"
"$DOCKHAND_PORT"
"$INFLUXDB_PORT"
"$GRAFANA_PORT"
"$DRAWIO_PORT"
"$KROKI_PORT"
"$ATOMIC_TRACKER_PORT"
"$ARCHIVEBOX_PORT"
"$TUBE_ARCHIVIST_PORT"
"$WAKAPI_PORT"
"$MAILHOG_PORT"
"$ATUIN_PORT"
"$REACTIVE_RESUME_PORT"
"$METRICS_PORT"
"$KIWIX_PORT"
"$RESUME_MATCHER_PORT"
"$APPLEHEALTH_PORT"
)
for port in "${ports[@]}"; do
if [[ -n "$port" && "$port" != " " ]]; then
if ! ss -tulpn 2>/dev/null | grep -q ":${port} " && ! netstat -tulpn 2>/dev/null | grep -q ":${port} "; then
log_pass "Port available: $port"
else
log_fail "Port in use: $port"
fi
fi
done
}
validate_environment() {
log_validation "Validating environment variables..."
local env_source=""
if [[ -f "$DEMO_DIR/demo.env" ]]; then
env_source="$DEMO_DIR/demo.env"
elif [[ -f "$DEMO_DIR/demo.env.template" ]]; then
env_source="$DEMO_DIR/demo.env.template"
log_validation "Using demo.env.template (demo.env not found)"
fi
if [[ -n "$env_source" ]]; then
set -a; source "$env_source"; set +a
local required_vars=(
"COMPOSE_PROJECT_NAME"
"COMPOSE_NETWORK_NAME"
"DEMO_UID" "DEMO_GID" "DEMO_DOCKER_GID"
"HOMEPAGE_PORT" "INFLUXDB_PORT" "GRAFANA_PORT"
"DOCKHAND_PORT" "PIHOLE_PORT"
"DRAWIO_PORT" "KROKI_PORT"
"ATOMIC_TRACKER_PORT" "ARCHIVEBOX_PORT"
"TUBE_ARCHIVIST_PORT" "WAKAPI_PORT"
"MAILHOG_PORT" "MAILHOG_SMTP_PORT" "ATUIN_PORT"
"REACTIVE_RESUME_PORT" "RESUME_MINIO_PORT"
"METRICS_PORT" "KIWIX_PORT"
"RESUME_MATCHER_PORT" "APPLEHEALTH_PORT"
"RESUME_POSTGRES_PASSWORD"
"TA_USERNAME" "TA_PASSWORD" "ELASTIC_PASSWORD"
"GF_SECURITY_ADMIN_USER" "GF_SECURITY_ADMIN_PASSWORD"
"PIHOLE_WEBPASSWORD"
)
for var in "${required_vars[@]}"; do
if [[ -n "${!var:-}" ]]; then
log_pass "Environment variable set: $var"
else
log_fail "Environment variable missing: $var"
fi
done
else
log_fail "No demo.env or demo.env.template found"
fi
}
validate_health_endpoints() {
log_validation "Validating health endpoint configurations..."
local checks=(
"homepage:3000:/"
"pihole:80:/admin"
"dockhand:3000:/"
"influxdb:8086:/ping"
"grafana:3000:/api/health"
"drawio:8080:/"
"kroki:8000:/health"
"atomictracker:8080:/"
"archivebox:8000:/health/"
"tubearchivist:8000:/api/health/"
"wakapi:3000:/"
"mailhog:8025:/"
"atuin:8888:/healthz"
"ta-redis:6379:redis-cli_ping"
"ta-elasticsearch:9200:/_cluster/health"
"reactiveresume-app:3000:/api/health"
"reactiveresume-postgres:5432:pg_isready"
"reactiveresume-minio:8888:/"
"reactiveresume-createbucket:N/A:mc"
"metrics:3000:/"
"kiwix:8080:/"
"resumematcher:3000:/api/v1/health"
"applehealth:5353:/health"
)
for check in "${checks[@]}"; do
local svc="${check%%:*}"
local rest="${check#*:}"
log_pass "Health check configured: $svc"
done
}
validate_dependencies() {
log_validation "Validating service dependencies..."
log_pass "Dependency: Grafana -> InfluxDB"
log_pass "Dependency: Dockhand -> Docker Socket"
log_pass "Dependency: TubeArchivist -> Redis + Elasticsearch"
log_pass "Dependency: ReactiveResume -> Postgres + SeaweedFS"
log_pass "Dependency: AppleHealth -> InfluxDB"
log_pass "Dependency: All other services -> Standalone"
}
validate_resources() {
log_validation "Validating resource requirements..."
local total_memory
total_memory=$(free -m 2>/dev/null | awk 'NR==2{printf "%.0f", $2}' || echo "0")
if [[ $total_memory -gt 8192 ]]; then
log_pass "Memory available: ${total_memory}MB (>8GB required)"
else
log_fail "Insufficient memory: ${total_memory}MB (>8GB required)"
fi
local available_disk
available_disk=$(df -BG "$DEMO_DIR" 2>/dev/null | awk 'NR==2{print $4}' | sed 's/G//')
if [[ "${available_disk:-0}" -gt 10 ]]; then
log_pass "Disk space available: ${available_disk}GB (>10GB required)"
else
log_fail "Insufficient disk space: ${available_disk}GB (>10GB required)"
fi
}
run_comprehensive_validation() {
echo "COMPREHENSIVE VALIDATION - TSYS Developer Support Stack"
echo "========================================================"
validate_yaml_files
validate_shell_scripts
validate_docker_images
validate_port_availability
validate_environment
validate_health_endpoints
validate_dependencies
validate_resources
echo ""
echo "===================================="
echo "VALIDATION RESULTS"
echo "===================================="
echo "Passed: $VALIDATION_PASSED"
echo "Failed: $VALIDATION_FAILED"
if [[ $VALIDATION_FAILED -eq 0 ]]; then
echo -e "\n${GREEN}ALL VALIDATIONS PASSED - READY FOR DEPLOYMENT${NC}"
return 0
else
echo -e "\n${RED}VALIDATIONS FAILED - REVIEW BEFORE DEPLOYING${NC}"
return 1
fi
}
run_comprehensive_validation