#!/usr/bin/env python3 """ Docker Build and Compose Test Script Tests that each service can be built correctly and docker-compose starts without errors """ import os import sys import subprocess import time import json from pathlib import Path def run_command(cmd, cwd=None, timeout=300, capture_output=True): """Run a shell command with timeout and error handling""" try: print(f"Running: {cmd}") if capture_output: result = subprocess.run( cmd, shell=True, cwd=cwd, timeout=timeout, capture_output=True, text=True ) else: result = subprocess.run( cmd, shell=True, cwd=cwd, timeout=timeout ) return result except subprocess.TimeoutExpired: print(f"Command timed out after {timeout} seconds: {cmd}") return None except Exception as e: print(f"Error running command: {e}") return None def check_docker_available(): """Check if Docker is available and running""" print("๐Ÿณ Checking Docker availability...") # Check if docker command exists result = run_command("which docker") if result.returncode != 0: print("โŒ Docker command not found. Please install Docker.") return False # Check if Docker daemon is running result = run_command("docker version") if result.returncode != 0: print("โŒ Docker daemon is not running. Please start Docker.") return False # Check if docker-compose is available result = run_command("docker compose version") if result.returncode != 0: # Try legacy docker-compose result = run_command("docker-compose version") if result.returncode != 0: print("โŒ docker-compose not found. Please install docker-compose.") return False else: print("โœ… Using legacy docker-compose") else: print("โœ… Using Docker Compose v2") print("โœ… Docker is available and running") return True def test_individual_builds(): """Test building each service individually""" print("\n๐Ÿ”จ Testing individual service builds...") services = [ "auth-service", "tenant-service", "training-service", "forecasting-service", "data-service", "notification-service" ] build_results = {} for service in services: print(f"\n--- Building {service} ---") dockerfile_path = f"./services/{service.replace('-service', '')}/Dockerfile" if not os.path.exists(dockerfile_path): print(f"โŒ Dockerfile not found: {dockerfile_path}") build_results[service] = False continue # Build the service cmd = f"docker build -t bakery/{service}:test -f {dockerfile_path} ." result = run_command(cmd, timeout=600) if result and result.returncode == 0: print(f"โœ… {service} built successfully") build_results[service] = True else: print(f"โŒ {service} build failed") if result and result.stderr: print(f"Error: {result.stderr}") build_results[service] = False return build_results def test_docker_compose_config(): """Test docker-compose configuration""" print("\n๐Ÿ“‹ Testing docker-compose configuration...") # Check if docker-compose.yml exists if not os.path.exists("docker-compose.yml"): print("โŒ docker-compose.yml not found") return False # Check if .env file exists if not os.path.exists(".env"): print("โŒ .env file not found") return False # Validate docker-compose configuration result = run_command("docker compose config") if result.returncode != 0: # Try legacy docker-compose result = run_command("docker-compose config") if result.returncode != 0: print("โŒ docker-compose configuration validation failed") if result.stderr: print(f"Error: {result.stderr}") return False print("โœ… docker-compose configuration is valid") return True def test_essential_services_startup(): """Test starting essential infrastructure services only""" print("\n๐Ÿš€ Testing essential services startup...") # Start only databases and infrastructure - not the application services essential_services = [ "redis", "rabbitmq", "auth-db", "data-db" ] try: # Stop any running containers first print("Stopping any existing containers...") run_command("docker compose down", timeout=120) # Start essential services services_str = " ".join(essential_services) cmd = f"docker compose up -d {services_str}" result = run_command(cmd, timeout=300) if result.returncode != 0: print("โŒ Failed to start essential services") if result.stderr: print(f"Error: {result.stderr}") return False # Wait for services to be ready print("Waiting for services to be ready...") time.sleep(30) # Check service health print("Checking service health...") result = run_command("docker compose ps") if result.returncode == 0: print("Services status:") print(result.stdout) print("โœ… Essential services started successfully") return True except Exception as e: print(f"โŒ Error during essential services test: {e}") return False finally: # Cleanup print("Cleaning up essential services test...") run_command("docker compose down", timeout=120) def cleanup_docker_resources(): """Clean up Docker resources""" print("\n๐Ÿงน Cleaning up Docker resources...") # Remove test images services = ["auth-service", "tenant-service", "training-service", "forecasting-service", "data-service", "notification-service"] for service in services: run_command(f"docker rmi bakery/{service}:test", timeout=30) # Remove dangling images run_command("docker image prune -f", timeout=60) print("โœ… Docker resources cleaned up") def main(): """Main test function""" print("๐Ÿ” DOCKER BUILD AND COMPOSE TESTING") print("=" * 50) base_path = Path(__file__).parent os.chdir(base_path) # Test results test_results = { "docker_available": False, "compose_config": False, "individual_builds": {}, "essential_services": False } # Check Docker availability test_results["docker_available"] = check_docker_available() if not test_results["docker_available"]: print("\nโŒ Docker tests cannot proceed without Docker") return 1 # Test docker-compose configuration test_results["compose_config"] = test_docker_compose_config() # Test individual builds (this might take a while) print("\nโš ๏ธ Individual builds test can take several minutes...") user_input = input("Run individual service builds? (y/N): ").lower() if user_input == 'y': test_results["individual_builds"] = test_individual_builds() else: print("Skipping individual builds test") test_results["individual_builds"] = {"skipped": True} # Test essential services startup print("\nโš ๏ธ Essential services test will start Docker containers...") user_input = input("Test essential services startup? (y/N): ").lower() if user_input == 'y': test_results["essential_services"] = test_essential_services_startup() else: print("Skipping essential services test") test_results["essential_services"] = "skipped" # Print final results print("\n" + "=" * 50) print("๐Ÿ“‹ TEST RESULTS SUMMARY") print("=" * 50) print(f"Docker Available: {'โœ…' if test_results['docker_available'] else 'โŒ'}") print(f"Docker Compose Config: {'โœ…' if test_results['compose_config'] else 'โŒ'}") if "skipped" in test_results["individual_builds"]: print("Individual Builds: โญ๏ธ Skipped") else: builds = test_results["individual_builds"] success_count = sum(1 for v in builds.values() if v) total_count = len(builds) print(f"Individual Builds: {success_count}/{total_count} โœ…") for service, success in builds.items(): status = "โœ…" if success else "โŒ" print(f" - {service}: {status}") if test_results["essential_services"] == "skipped": print("Essential Services: โญ๏ธ Skipped") else: print(f"Essential Services: {'โœ…' if test_results['essential_services'] else 'โŒ'}") # Cleanup if user_input == 'y' and "skipped" not in test_results["individual_builds"]: cleanup_docker_resources() # Determine overall success if (test_results["docker_available"] and test_results["compose_config"] and (test_results["individual_builds"] == {"skipped": True} or all(test_results["individual_builds"].values())) and (test_results["essential_services"] == "skipped" or test_results["essential_services"])): print("\n๐ŸŽ‰ ALL TESTS PASSED") return 0 else: print("\nโŒ SOME TESTS FAILED") return 1 if __name__ == "__main__": exit_code = main() sys.exit(exit_code)