| |
| """ |
| Test Docker Compose Configurations |
| Test the new Docker Compose setups for local and modal deployments |
| """ |
|
|
| import os |
| import sys |
| import subprocess |
| import time |
| import yaml |
| import tempfile |
|
|
| def test_compose_file_validity(): |
| """Test that Docker Compose files are valid YAML""" |
| print("π Testing Docker Compose file validity...") |
| |
| compose_files = [ |
| "docker-compose.local.yml", |
| "docker-compose.modal.yml" |
| ] |
| |
| for compose_file in compose_files: |
| try: |
| with open(compose_file, 'r') as f: |
| yaml.safe_load(f) |
| print(f"β
{compose_file} is valid YAML") |
| except yaml.YAMLError as e: |
| print(f"β {compose_file} invalid YAML: {e}") |
| return False |
| except FileNotFoundError: |
| print(f"β {compose_file} not found") |
| return False |
| |
| return True |
|
|
| def test_environment_variables(): |
| """Test environment variable handling in compose files""" |
| print("\nπ Testing environment variable defaults...") |
| |
| |
| try: |
| with open("docker-compose.local.yml", 'r') as f: |
| local_content = f.read() |
| |
| |
| env_patterns = [ |
| "${GRADIO_PORT:-7860}", |
| "${A2A_API_PORT:-8000}", |
| "${OLLAMA_PORT:-11434}", |
| "${FHIRFLAME_DEV_MODE:-true}", |
| "${HF_TOKEN}", |
| "${MISTRAL_API_KEY}" |
| ] |
| |
| for pattern in env_patterns: |
| if pattern in local_content: |
| print(f"β
Local compose has: {pattern}") |
| else: |
| print(f"β Local compose missing: {pattern}") |
| return False |
| |
| |
| with open("docker-compose.modal.yml", 'r') as f: |
| modal_content = f.read() |
| |
| modal_patterns = [ |
| "${MODAL_TOKEN_ID}", |
| "${MODAL_TOKEN_SECRET}", |
| "${MODAL_ENDPOINT_URL}", |
| "${MODAL_L4_HOURLY_RATE:-0.73}", |
| "${AUTH0_DOMAIN:-}" |
| ] |
| |
| for pattern in modal_patterns: |
| if pattern in modal_content: |
| print(f"β
Modal compose has: {pattern}") |
| else: |
| print(f"β Modal compose missing: {pattern}") |
| return False |
| |
| return True |
| |
| except Exception as e: |
| print(f"β Environment variable test failed: {e}") |
| return False |
|
|
| def test_compose_services(): |
| """Test that required services are defined""" |
| print("\nπ Testing service definitions...") |
| |
| try: |
| |
| with open("docker-compose.local.yml", 'r') as f: |
| local_config = yaml.safe_load(f) |
| |
| local_services = local_config.get('services', {}) |
| required_local_services = [ |
| 'fhirflame-local', |
| 'fhirflame-a2a-api', |
| 'ollama', |
| 'ollama-setup' |
| ] |
| |
| for service in required_local_services: |
| if service in local_services: |
| print(f"β
Local has service: {service}") |
| else: |
| print(f"β Local missing service: {service}") |
| return False |
| |
| |
| with open("docker-compose.modal.yml", 'r') as f: |
| modal_config = yaml.safe_load(f) |
| |
| modal_services = modal_config.get('services', {}) |
| required_modal_services = [ |
| 'fhirflame-modal', |
| 'fhirflame-a2a-modal', |
| 'modal-deployer' |
| ] |
| |
| for service in required_modal_services: |
| if service in modal_services: |
| print(f"β
Modal has service: {service}") |
| else: |
| print(f"β Modal missing service: {service}") |
| return False |
| |
| return True |
| |
| except Exception as e: |
| print(f"β Service definition test failed: {e}") |
| return False |
|
|
| def test_port_configurations(): |
| """Test port configurations and conflicts""" |
| print("\nπ Testing port configurations...") |
| |
| try: |
| |
| with open("docker-compose.local.yml", 'r') as f: |
| local_config = yaml.safe_load(f) |
| |
| local_ports = [] |
| for service_name, service_config in local_config['services'].items(): |
| ports = service_config.get('ports', []) |
| for port_mapping in ports: |
| if isinstance(port_mapping, str): |
| host_port = port_mapping.split(':')[0] |
| |
| if 'GRADIO_PORT:-7860' in host_port: |
| local_ports.append('7860') |
| elif 'A2A_API_PORT:-8000' in host_port: |
| local_ports.append('8000') |
| elif 'OLLAMA_PORT:-11434' in host_port: |
| local_ports.append('11434') |
| |
| print(f"β
Local default ports: {', '.join(local_ports)}") |
| |
| |
| with open("docker-compose.modal.yml", 'r') as f: |
| modal_config = yaml.safe_load(f) |
| |
| modal_ports = [] |
| for service_name, service_config in modal_config['services'].items(): |
| ports = service_config.get('ports', []) |
| for port_mapping in ports: |
| if isinstance(port_mapping, str): |
| host_port = port_mapping.split(':')[0] |
| if 'GRADIO_PORT:-7860' in host_port: |
| modal_ports.append('7860') |
| elif 'A2A_API_PORT:-8000' in host_port: |
| modal_ports.append('8000') |
| |
| print(f"β
Modal default ports: {', '.join(modal_ports)}") |
| |
| return True |
| |
| except Exception as e: |
| print(f"β Port configuration test failed: {e}") |
| return False |
|
|
| def test_compose_validation(): |
| """Test Docker Compose file validation using docker-compose""" |
| print("\nπ Testing Docker Compose validation...") |
| |
| compose_files = [ |
| "docker-compose.local.yml", |
| "docker-compose.modal.yml" |
| ] |
| |
| for compose_file in compose_files: |
| try: |
| |
| result = subprocess.run([ |
| "docker-compose", "-f", compose_file, "config" |
| ], capture_output=True, text=True, timeout=30) |
| |
| if result.returncode == 0: |
| print(f"β
{compose_file} validates with docker-compose") |
| else: |
| print(f"β {compose_file} validation failed: {result.stderr}") |
| return False |
| |
| except subprocess.TimeoutExpired: |
| print(f"β οΈ {compose_file} validation timeout (docker-compose not available)") |
| except FileNotFoundError: |
| print(f"β οΈ docker-compose not found, skipping validation for {compose_file}") |
| except Exception as e: |
| print(f"β οΈ {compose_file} validation error: {e}") |
| |
| return True |
|
|
| def test_health_check_definitions(): |
| """Test that health checks are properly defined""" |
| print("\nπ Testing health check definitions...") |
| |
| try: |
| |
| with open("docker-compose.local.yml", 'r') as f: |
| local_config = yaml.safe_load(f) |
| |
| services_with_healthcheck = [] |
| for service_name, service_config in local_config['services'].items(): |
| if 'healthcheck' in service_config: |
| healthcheck = service_config['healthcheck'] |
| if 'test' in healthcheck: |
| services_with_healthcheck.append(service_name) |
| |
| print(f"β
Local services with health checks: {', '.join(services_with_healthcheck)}") |
| |
| |
| with open("docker-compose.modal.yml", 'r') as f: |
| modal_config = yaml.safe_load(f) |
| |
| modal_healthchecks = [] |
| for service_name, service_config in modal_config['services'].items(): |
| if 'healthcheck' in service_config: |
| modal_healthchecks.append(service_name) |
| |
| print(f"β
Modal services with health checks: {', '.join(modal_healthchecks)}") |
| |
| return True |
| |
| except Exception as e: |
| print(f"β Health check test failed: {e}") |
| return False |
|
|
| def main(): |
| """Run all Docker Compose tests""" |
| print("π³ Testing Docker Compose Configurations") |
| print("=" * 50) |
| |
| tests = [ |
| ("YAML Validity", test_compose_file_validity), |
| ("Environment Variables", test_environment_variables), |
| ("Service Definitions", test_compose_services), |
| ("Port Configurations", test_port_configurations), |
| ("Compose Validation", test_compose_validation), |
| ("Health Checks", test_health_check_definitions) |
| ] |
| |
| results = {} |
| |
| for test_name, test_func in tests: |
| try: |
| result = test_func() |
| results[test_name] = result |
| except Exception as e: |
| print(f"β {test_name} crashed: {e}") |
| results[test_name] = False |
| |
| |
| print("\n" + "=" * 50) |
| print("π Docker Compose Test Results") |
| print("=" * 50) |
| |
| passed = sum(1 for r in results.values() if r) |
| total = len(results) |
| |
| for test_name, result in results.items(): |
| status = "β
PASS" if result else "β FAIL" |
| print(f"{test_name}: {status}") |
| |
| print(f"\nOverall: {passed}/{total} tests passed") |
| |
| if passed == total: |
| print("\nπ All Docker Compose tests passed!") |
| print("\nπ Deployment Commands:") |
| print("π Local: docker-compose -f docker-compose.local.yml up") |
| print("βοΈ Modal: docker-compose -f docker-compose.modal.yml up") |
| print("π§ͺ Test Local: docker-compose -f docker-compose.local.yml --profile test up") |
| print("π Deploy Modal: docker-compose -f docker-compose.modal.yml --profile deploy up") |
| else: |
| print("\nβ οΈ Some Docker Compose tests failed.") |
| |
| return passed == total |
|
|
| if __name__ == "__main__": |
| |
| os.chdir(os.path.dirname(os.path.dirname(__file__))) |
| success = main() |
| sys.exit(0 if success else 1) |