Files
TSYSDevStack-SupportStack-L…/demo/tests/unit/test_env_validation.sh
reachableceo 25f7a6cd75 feat(demo): migrate 5 SelfStack services to demo stack (16→24 services)
Add Reactive Resume, Metrics, Kiwix, Resume Matcher, and Apple Health
from the earlier SelfStack project. Rewrite Apple Health collector to
use InfluxDB v2 with proper error handling. Update all tests, scripts,
Homepage config, env template, and documentation for the expanded stack.

New services:
- Reactive Resume (4016) + Postgres/Minio/Chrome companions
- Metrics (4021) - GitHub metrics visualization
- Kiwix (4022) - offline wiki reader
- Resume Matcher (4023) - AI resume screening
- Apple Health (4024) - health data collector → InfluxDB v2

Also adds git policy to AGENTS.md: always commit and push automatically.

💘 Generated with Crush

Assisted-by: GLM-5.1 via Crush <crush@charm.land>
2026-05-08 12:28:56 -05:00

269 lines
9.2 KiB
Bash
Executable File

#!/bin/bash
# Unit test: Environment and configuration validation
# These tests validate the project configuration without requiring Docker.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$(dirname "$SCRIPT_DIR")")"
TEMPLATE_FILE="$PROJECT_ROOT/docker-compose.yml.template"
ENV_TEMPLATE="$PROJECT_ROOT/demo.env.template"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
PASS=0
FAIL=0
pass() { echo -e "${GREEN}[PASS]${NC} $1"; ((PASS++)) || true; }
fail() { echo -e "${RED}[FAIL]${NC} $1"; ((FAIL++)) || true; }
check() { echo -e "${YELLOW}[CHECK]${NC} $1"; }
grep_exists() {
grep "$@" >/dev/null 2>&1 || true
}
test_template_exists() {
check "docker-compose.yml.template exists"
if [[ -f "$TEMPLATE_FILE" ]]; then
pass "Template file exists"
else
fail "Template file not found at $TEMPLATE_FILE"
fi
}
test_template_has_required_sections() {
check "Template has required top-level sections"
local sections=("networks:" "volumes:" "services:")
for section in "${sections[@]}"; do
if grep_exists "^$section" "$TEMPLATE_FILE"; then
pass "Template contains '$section' section"
else
fail "Template missing '$section' section"
fi
done
}
test_template_has_all_services() {
check "Template defines all 24 services"
local services=(
"docker-socket-proxy:" "homepage:" "pihole:" "dockhand:"
"influxdb:" "grafana:" "drawio:" "kroki:" "atomictracker:"
"archivebox:" "ta-redis:" "ta-elasticsearch:" "tubearchivist:"
"wakapi:" "mailhog:" "atuin:"
"reactiveresume-postgres:" "reactiveresume-minio:" "reactiveresume-chrome:" "reactiveresume-app:" "metrics:" "kiwix:" "resumematcher:" "applehealth:"
)
local found=0
for svc in "${services[@]}"; do
if grep_exists " ${svc}" "$TEMPLATE_FILE"; then
((found++)) || true
else
fail "Service not found in template: $svc"
fi
done
if [[ $found -eq ${#services[@]} ]]; then
pass "All ${#services[@]} services defined in template"
fi
}
test_all_services_have_healthchecks() {
check "All exposed services have healthcheck blocks"
local exposed_services=("homepage" "pihole" "dockhand" "influxdb" "grafana" "drawio" "kroki" "atomictracker" "archivebox" "tubearchivist" "wakapi" "mailhog" "atuin" "reactiveresume-app" "metrics" "kiwix" "resumematcher" "applehealth")
local missing=()
for svc in "${exposed_services[@]}"; do
local svc_block
svc_block=$(sed -n "/^ ${svc}:/,/^[^ ]/p" "$TEMPLATE_FILE" || true)
if echo "$svc_block" | grep_exists "healthcheck:"; then
:
else
missing+=("$svc")
fi
done
if [[ ${#missing[@]} -eq 0 ]]; then
pass "All exposed services have health checks"
else
fail "Services missing health checks: ${missing[*]}"
fi
}
test_all_services_have_restart_policy() {
check "All services have restart policy"
local restart_count
restart_count=$(grep -c "restart:" "$TEMPLATE_FILE" || true)
if [[ $restart_count -ge 24 ]]; then
pass "$restart_count services have restart policies"
else
fail "Only $restart_count services have restart policies (expected >= 24)"
fi
}
test_all_services_have_labels() {
check "All user-facing services have Homepage labels"
local label_services=("homepage" "pihole" "dockhand" "influxdb" "grafana" "drawio" "kroki" "atomictracker" "archivebox" "tubearchivist" "wakapi" "mailhog" "atuin" "reactiveresume-app" "metrics" "kiwix" "resumematcher" "applehealth")
local missing=()
for svc in "${label_services[@]}"; do
local svc_block
svc_block=$(sed -n "/^ ${svc}:/,/^[^ ]/p" "$TEMPLATE_FILE" || true)
if echo "$svc_block" | grep_exists "homepage.group:"; then
:
else
missing+=("$svc")
fi
done
if [[ ${#missing[@]} -eq 0 ]]; then
pass "All user-facing services have Homepage discovery labels"
else
fail "Services missing labels: ${missing[*]}"
fi
}
test_dockhand_uses_proxy() {
check "Dockhand connects through docker-socket-proxy"
local dockhand_block
dockhand_block=$(sed -n "/^ dockhand:/,/^[^ ]/p" "$TEMPLATE_FILE" || true)
if echo "$dockhand_block" | grep_exists "DOCKER_HOST=tcp://docker-socket-proxy:2375"; then
pass "Dockhand routes through socket proxy"
else
fail "Dockhand not configured to use socket proxy (security issue)"
fi
}
test_no_direct_socket_mounts_except_proxy() {
check "No direct Docker socket mounts except on socket-proxy"
local socket_lines
socket_lines=$(grep -n '/var/run/docker\.sock' "$TEMPLATE_FILE" || true)
local bad_mounts=0
while IFS= read -r line; do
[[ -z "$line" ]] && continue
local line_num
line_num=$(echo "$line" | cut -d: -f1)
local context
context=$(head -n "$line_num" "$TEMPLATE_FILE" | grep "^ [a-z]" | tail -1 || true)
if [[ "$context" != *"docker-socket-proxy"* ]]; then
((bad_mounts++)) || true
fail "Direct socket mount found outside proxy at line $line_num"
fi
done <<< "$socket_lines"
if [[ $bad_mounts -eq 0 ]]; then
pass "Only docker-socket-proxy mounts the Docker socket"
fi
}
test_env_template_completeness() {
check "demo.env.template has all required variables"
local required_vars=(
"COMPOSE_PROJECT_NAME" "COMPOSE_NETWORK_NAME"
"DEMO_UID" "DEMO_GID" "DEMO_DOCKER_GID"
"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" "MAILHOG_SMTP_PORT" "ATUIN_PORT"
"NETWORK_SUBNET" "NETWORK_GATEWAY"
"TA_USERNAME" "TA_PASSWORD" "ELASTIC_PASSWORD"
"GF_SECURITY_ADMIN_USER" "GF_SECURITY_ADMIN_PASSWORD"
"PIHOLE_WEBPASSWORD"
"REACTIVE_RESUME_PORT" "RESUME_MINIO_PORT" "METRICS_PORT" "KIWIX_PORT" "RESUME_MATCHER_PORT" "APPLEHEALTH_PORT" "RESUME_POSTGRES_PASSWORD"
)
for var in "${required_vars[@]}"; do
if grep_exists "^${var}=" "$ENV_TEMPLATE"; then
pass "Env template has $var"
else
fail "Env template missing $var"
fi
done
}
test_env_template_port_range() {
check "All ports in env template are in 4000-4099 range"
local ports_out_of_range=()
while IFS='=' read -r var val; do
if [[ "$var" == *"_PORT" && "$val" =~ ^[0-9]+$ ]]; then
if [[ "$val" -lt 4000 || "$val" -gt 4099 ]]; then
ports_out_of_range+=("$var=$val")
fi
fi
done < "$ENV_TEMPLATE"
if [[ ${#ports_out_of_range[@]} -eq 0 ]]; then
pass "All ports within 4000-4099 range"
else
fail "Ports outside range: ${ports_out_of_range[*]}"
fi
}
test_homepage_configs_exist() {
check "Homepage configuration files exist"
local configs=("services.yaml" "widgets.yaml" "settings.yaml" "bookmarks.yaml" "docker.yaml")
for cfg in "${configs[@]}"; do
if [[ -f "$PROJECT_ROOT/config/homepage/$cfg" ]]; then
pass "Homepage config exists: $cfg"
else
fail "Homepage config missing: $cfg"
fi
done
}
test_grafana_configs_exist() {
check "Grafana configuration files exist"
local configs=("datasources.yml" "dashboards.yml" "dashboards/docker-overview.json")
for cfg in "${configs[@]}"; do
if [[ -f "$PROJECT_ROOT/config/grafana/$cfg" ]]; then
pass "Grafana config exists: $cfg"
else
fail "Grafana config missing: $cfg"
fi
done
}
test_scripts_exist() {
check "Deployment scripts exist"
local scripts=("scripts/demo-stack.sh" "scripts/demo-test.sh" "scripts/validate-all.sh")
for script in "${scripts[@]}"; do
if [[ -f "$PROJECT_ROOT/$script" ]]; then
pass "Script exists: $script"
else
fail "Script missing: $script"
fi
done
}
test_scripts_use_strict_mode() {
check "All scripts use strict mode (set -euo pipefail)"
local found_scripts
found_scripts=("$PROJECT_ROOT/scripts/"*.sh)
for script in "${found_scripts[@]}"; do
if head -5 "$script" | grep_exists "set -euo pipefail"; then
pass "$(basename "$script") uses strict mode"
else
fail "$(basename "$script") missing strict mode"
fi
done
}
echo "======================================"
echo "Unit Tests: Configuration Validation"
echo "======================================"
echo ""
test_template_exists
test_template_has_required_sections
test_template_has_all_services
test_all_services_have_healthchecks
test_all_services_have_restart_policy
test_all_services_have_labels
test_dockhand_uses_proxy
test_no_direct_socket_mounts_except_proxy
test_env_template_completeness
test_env_template_port_range
test_homepage_configs_exist
test_grafana_configs_exist
test_scripts_exist
test_scripts_use_strict_mode
echo ""
echo "======================================"
echo "RESULTS: $PASS passed, $FAIL failed"
echo "======================================"
[[ $FAIL -eq 0 ]]