from __future__ import annotations import subprocess import sys import tempfile import unittest from pathlib import Path ROOT = Path(__file__).resolve().parents[1] class InfrastructureConfigTests(unittest.TestCase): def test_root_compose_uses_only_infra_fragments(self) -> None: compose = (ROOT / "docker-compose.yml").read_text(encoding="utf-8") for fragment in ( "infra/compose/application.yml", "nginx/docker-compose.yml", "redis/docker-compose.yml", "observability/docker-compose.yml", "deployment/docker-compose.jobs.yml", ): self.assertIn(fragment, compose) self.assertNotIn("postgres:", compose.lower()) def test_only_nginx_fragment_publishes_ports(self) -> None: forbidden = ( ROOT / "infra/compose/application.yml", ROOT / "redis/docker-compose.yml", ROOT / "observability/docker-compose.yml", ROOT / "deployment/docker-compose.jobs.yml", ) for path in forbidden: self.assertNotIn("\n ports:", path.read_text(encoding="utf-8"), path) nginx = (ROOT / "nginx/docker-compose.yml").read_text(encoding="utf-8") self.assertEqual(nginx.count("\n ports:"), 1) self.assertIn('NGINX_HTTP_PORT:-80}:80', nginx) self.assertIn('NGINX_HTTPS_PORT:-443}:443', nginx) def test_nginx_internal_denies_precede_spa(self) -> None: site = (ROOT / "nginx/templates/site-tls.conf.template").read_text(encoding="utf-8") internal = site.index("location ^~ /internal/") api = site.index("location ^~ /api/") frontend = site.index("include /etc/nginx/generated/frontend-location.conf") self.assertLess(internal, api) self.assertLess(api, frontend) self.assertIn("location = /api/v1/realtime", site) self.assertNotIn("message-safety:", site) def test_redis_persistence_acl_and_no_host_port(self) -> None: config = (ROOT / "redis/redis.conf").read_text(encoding="utf-8") acl = (ROOT / "redis/users.acl.template").read_text(encoding="utf-8") self.assertIn("appendonly yes", config) self.assertIn("appendfsync everysec", config) self.assertIn("save 900 1", config) self.assertIn("user default off", acl) self.assertIn("~han:api:*", acl) self.assertIn("~han:safety:*", acl) def test_otel_has_redaction_and_persistent_queue(self) -> None: config = (ROOT / "observability/otel-collector.yaml").read_text(encoding="utf-8") for forbidden_attribute in ( "http.request.header.authorization", "http.request.header.cookie", "url.query", "db.statement", "messaging.message.body", ): self.assertIn(forbidden_attribute, config) self.assertIn("storage: file_storage", config) self.assertIn("retry_on_failure:", config) def test_settings_cli_and_workers_are_deployable(self) -> None: cli = ROOT / "api-backend/app/cli" self.assertTrue((cli / "__init__.py").is_file()) self.assertTrue((cli / "seed_settings.py").is_file()) self.assertTrue((cli / "validate_settings.py").is_file()) jobs = (ROOT / "deployment/docker-compose.jobs.yml").read_text(encoding="utf-8") self.assertIn("python -m app.cli.seed_settings", jobs) self.assertIn("python -m app.cli.validate_settings", jobs) application = (ROOT / "infra/compose/application.yml").read_text(encoding="utf-8") self.assertIn("\n frontend-static:", application) self.assertIn("frontend-test-site", application) self.assertIn("frontend-static:/output", application) for service, command in ( ("delivery-worker:", "han-delivery-worker"), ("safety-recovery-worker:", "han-safety-worker"), ("cleanup-worker:", "han-cleanup-worker"), ): self.assertIn(f"\n {service}", application) self.assertIn(f'command: ["{command}"]', application) self.assertNotIn("\n ports:", application) nginx = (ROOT / "nginx/docker-compose.yml").read_text(encoding="utf-8") self.assertIn("frontend-static: {condition: service_completed_successfully}", nginx) def test_keycloak_management_health_and_bridge_environment(self) -> None: standalone = (ROOT / "keycloak/docker-compose.yml").read_text(encoding="utf-8") self.assertIn("GET /health/ready", standalone) self.assertNotIn("GET /auth/health/ready", standalone) application = (ROOT / "infra/compose/application.yml").read_text(encoding="utf-8") for variable in ( "KEYCLOAK_OTP_MOCK_ENABLED", "KEYCLOAK_OTP_MOCK_CODE", "KEYCLOAK_OTP_HMAC_KEY", "KEYCLOAK_OTP_TTL_SEC", "KEYCLOAK_OTP_MAX_VERIFY_ATTEMPTS", "KEYCLOAK_OTP_SETTINGS_MAX_STALE_SEC", "KEYCLOAK_SETTINGS_BRIDGE_URL", "KEYCLOAK_SETTINGS_BRIDGE_TOKEN", ): self.assertIn(f" {variable}:", application) def test_env_validator_accepts_materialized_example(self) -> None: example = (ROOT / ".env.example").read_text(encoding="utf-8") for required in ( "CURSOR_HMAC_SECRET=", "BITRIX_TOKEN_ENCRYPTION_KEY=", "KEYCLOAK_INTERNAL_URL=http://keycloak:8080/auth", ): self.assertIn(required, example) materialized = example.replace("change-me", "0123456789abcdef0123456789abcdef") materialized = materialized.replace( "KEYCLOAK_OTP_MOCK_RISK_ACCEPTED=false", "KEYCLOAK_OTP_MOCK_RISK_ACCEPTED=true", ) with tempfile.TemporaryDirectory() as directory: env_file = Path(directory) / ".env" env_file.write_text(materialized, encoding="utf-8") result = subprocess.run( [sys.executable, str(ROOT / "scripts/validate-env"), str(env_file)], text=True, capture_output=True, check=False, ) self.assertEqual(result.returncode, 0, result.stderr) if __name__ == "__main__": unittest.main()