#!/bin/bash # TSYS Developer Support Stack - Demo Testing Script # Version: 2.0 # Purpose: Comprehensive QA and validation set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" DEMO_ENV_FILE="$PROJECT_ROOT/demo.env" COMPOSE_FILE="$PROJECT_ROOT/docker-compose.yml" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' TESTS_PASSED=0 TESTS_FAILED=0 TESTS_TOTAL=0 log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[PASS]${NC} $1"; ((TESTS_PASSED++)) || true; } log_warning() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[FAIL]${NC} $1"; ((TESTS_FAILED++)) || true; } log_test() { echo -e "${BLUE}[TEST]${NC} $1"; ((TESTS_TOTAL++)) || true; } test_file_ownership() { log_test "File ownership (no root-owned files)" local root_files root_files=$(find "$PROJECT_ROOT" -type f -user root 2>/dev/null || true) if [[ -z "$root_files" ]]; then log_success "No root-owned files" else log_error "Root-owned files found: $root_files" fi } test_user_mapping() { log_test "UID/GID detection" source "$DEMO_ENV_FILE" if [[ -z "${DEMO_UID:-}" || -z "${DEMO_GID:-}" ]]; then log_error "DEMO_UID or DEMO_GID not set" return fi local cur_uid cur_gid cur_uid=$(id -u) cur_gid=$(id -g) if [[ "$DEMO_UID" -eq "$cur_uid" && "$DEMO_GID" -eq "$cur_gid" ]]; then log_success "UID/GID correct ($DEMO_UID/$DEMO_GID)" else log_error "UID/GID mismatch: env=$DEMO_UID/$DEMO_GID actual=$cur_uid/$cur_gid" fi } test_docker_group() { log_test "Docker group access" source "$DEMO_ENV_FILE" if [[ -z "${DEMO_DOCKER_GID:-}" ]]; then log_error "DEMO_DOCKER_GID not set" return fi local actual_gid actual_gid=$(getent group docker | cut -d: -f3) if [[ "$DEMO_DOCKER_GID" -eq "$actual_gid" ]]; then log_success "Docker GID correct ($DEMO_DOCKER_GID)" else log_error "Docker GID mismatch: env=$DEMO_DOCKER_GID actual=$actual_gid" fi } test_service_health() { log_test "Service health" local unhealthy=0 while IFS= read -r line; do local name status name=$(echo "$line" | awk '{print $1}') [[ "$name" == "NAMES" || -z "$name" ]] && continue if echo "$line" | grep -q "(healthy)"; then log_success "$name healthy" elif echo "$line" | grep -q "Up"; then log_success "$name running" else log_error "$name not running: $line" ((unhealthy++)) || true fi done < <(docker ps --filter "name=${COMPOSE_PROJECT_NAME:-kneldevstack}" --format "{{.Names}} {{.Status}}" 2>/dev/null) if [[ $unhealthy -eq 0 ]]; then log_success "All services running" fi } test_port_accessibility() { log_test "Port accessibility" source "$DEMO_ENV_FILE" # These are exposed to host local port_tests=( "$HOMEPAGE_PORT:Homepage" "$PIHOLE_PORT:Pi-hole" "$DOCKHAND_PORT:Dockhand" "$INFLUXDB_PORT:InfluxDB" "$GRAFANA_PORT:Grafana" "$DRAWIO_PORT:Draw.io" "$KROKI_PORT:Kroki" "$ATOMIC_TRACKER_PORT:AtomicTracker" "$ARCHIVEBOX_PORT:ArchiveBox" "$TUBE_ARCHIVIST_PORT:TubeArchivist" "$WAKAPI_PORT:Wakapi" "$MAILHOG_PORT:MailHog" "$ATUIN_PORT:Atuin" ) local failed=0 for pt in "${port_tests[@]}"; do local port="${pt%:*}" local svc="${pt#*:}" if timeout 5 bash -c "echo > /dev/tcp/localhost/$port" 2>/dev/null; then log_success "$svc (:$port)" else log_error "$svc (:$port) not accessible" ((failed++)) || true fi done if [[ $failed -eq 0 ]]; then log_success "All exposed ports accessible" fi } test_network_isolation() { log_test "Network isolation" source "$DEMO_ENV_FILE" if docker network ls --format '{{.Name}}' | grep -q "$COMPOSE_NETWORK_NAME"; then log_success "Network $COMPOSE_NETWORK_NAME exists" local driver driver=$(docker network inspect "$COMPOSE_NETWORK_NAME" --format '{{.Driver}}' 2>/dev/null || echo "") if [[ "$driver" == "bridge" ]]; then log_success "Bridge driver confirmed" else log_warning "Driver: $driver" fi else log_error "Network $COMPOSE_NETWORK_NAME not found" fi } test_volume_permissions() { log_test "Docker volumes exist" source "$DEMO_ENV_FILE" local vol_count vol_count=$(docker volume ls --filter "name=${COMPOSE_PROJECT_NAME}" -q 2>/dev/null | wc -l) if [[ $vol_count -ge 15 ]]; then log_success "$vol_count volumes created" else log_error "Only $vol_count volumes found" fi } test_security_compliance() { log_test "Security compliance" source "$DEMO_ENV_FILE" # Docker socket proxy present if grep -q "docker-socket-proxy" "$COMPOSE_FILE"; then log_success "Docker socket proxy configured" else log_error "Docker socket proxy not found" fi # Count direct socket mounts - only proxy should have one local socket_mounts socket_mounts=$(grep -c '/var/run/docker.sock' "$COMPOSE_FILE" || echo "0") if [[ "$socket_mounts" -le 1 ]]; then log_success "Socket mount on proxy only ($socket_mounts)" else log_error "Unexpected socket mounts: $socket_mounts (expected 1, proxy only)" fi # Dockhand uses proxy, not direct socket if grep -q 'DOCKER_HOST=tcp://docker-socket-proxy' "$COMPOSE_FILE"; then log_success "Dockhand routes through socket proxy" else log_error "Dockhand not using socket proxy" fi } run_full_tests() { log_info "Running comprehensive test suite..." test_file_ownership || true test_user_mapping || true test_docker_group || true test_service_health || true test_port_accessibility || true test_network_isolation || true test_volume_permissions || true test_security_compliance || true display_test_results } run_security_tests() { log_info "Running security tests..." test_file_ownership || true test_network_isolation || true test_security_compliance || true display_test_results } run_permission_tests() { log_info "Running permission tests..." test_file_ownership || true test_user_mapping || true test_docker_group || true test_volume_permissions || true display_test_results } run_network_tests() { log_info "Running network tests..." test_network_isolation || true test_port_accessibility || true display_test_results } display_test_results() { echo "" echo "====================================" echo "TEST RESULTS" echo "====================================" echo "Total: $TESTS_TOTAL" echo -e "Passed: ${GREEN}$TESTS_PASSED${NC}" echo -e "Failed: ${RED}$TESTS_FAILED${NC}" if [[ $TESTS_FAILED -eq 0 ]]; then echo -e "\n${GREEN}ALL TESTS PASSED${NC}" return 0 else echo -e "\n${RED}SOME TESTS FAILED${NC}" return 1 fi } main() { case "${1:-full}" in full) run_full_tests ;; security) run_security_tests ;; permissions) run_permission_tests ;; network) run_network_tests ;; help|--help|-h) echo "Usage: $0 {full|security|permissions|network|help}" ;; *) log_error "Unknown: $1"; exit 1 ;; esac } main "$@"