This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
breakpilot-pwa/backend/test_environment_config.py
BreakPilot Dev 19855efacc
Some checks failed
Tests / Go Tests (push) Has been cancelled
Tests / Python Tests (push) Has been cancelled
Tests / Integration Tests (push) Has been cancelled
Tests / Go Lint (push) Has been cancelled
Tests / Python Lint (push) Has been cancelled
Tests / Security Scan (push) Has been cancelled
Tests / All Checks Passed (push) Has been cancelled
Security Scanning / Secret Scanning (push) Has been cancelled
Security Scanning / Dependency Vulnerability Scan (push) Has been cancelled
Security Scanning / Go Security Scan (push) Has been cancelled
Security Scanning / Python Security Scan (push) Has been cancelled
Security Scanning / Node.js Security Scan (push) Has been cancelled
Security Scanning / Docker Image Security (push) Has been cancelled
Security Scanning / Security Summary (push) Has been cancelled
CI/CD Pipeline / Go Tests (push) Has been cancelled
CI/CD Pipeline / Python Tests (push) Has been cancelled
CI/CD Pipeline / Website Tests (push) Has been cancelled
CI/CD Pipeline / Linting (push) Has been cancelled
CI/CD Pipeline / Security Scan (push) Has been cancelled
CI/CD Pipeline / Docker Build & Push (push) Has been cancelled
CI/CD Pipeline / Integration Tests (push) Has been cancelled
CI/CD Pipeline / Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / Deploy to Production (push) Has been cancelled
CI/CD Pipeline / CI Summary (push) Has been cancelled
ci/woodpecker/manual/build-ci-image Pipeline was successful
ci/woodpecker/manual/main Pipeline failed
feat: BreakPilot PWA - Full codebase (clean push without large binaries)
All services: admin-v2, studio-v2, website, ai-compliance-sdk,
consent-service, klausur-service, voice-service, and infrastructure.
Large PDFs and compiled binaries excluded via .gitignore.
2026-02-11 13:25:58 +01:00

326 lines
13 KiB
Python

"""
Tests for Dev/Staging/Prod environment configuration.
Tests the environment files, Docker Compose configurations, and helper scripts
to ensure proper environment separation and functionality.
Usage:
cd backend && pytest test_environment_config.py -v
"""
import os
import subprocess
from pathlib import Path
from typing import Dict, List
import pytest
# Project root directory
PROJECT_ROOT = Path(__file__).parent.parent
class TestEnvironmentFiles:
"""Test environment configuration files exist and have correct content."""
def test_env_dev_exists(self):
"""Test that .env.dev file exists."""
env_file = PROJECT_ROOT / ".env.dev"
assert env_file.exists(), f".env.dev should exist at {env_file}"
def test_env_staging_exists(self):
"""Test that .env.staging file exists."""
env_file = PROJECT_ROOT / ".env.staging"
assert env_file.exists(), f".env.staging should exist at {env_file}"
def test_env_example_exists(self):
"""Test that .env.example file exists."""
env_file = PROJECT_ROOT / ".env.example"
assert env_file.exists(), f".env.example should exist at {env_file}"
def test_env_dev_content(self):
"""Test that .env.dev has correct environment settings."""
env_file = PROJECT_ROOT / ".env.dev"
if not env_file.exists():
pytest.skip(".env.dev not found")
content = env_file.read_text()
# Required settings for development
assert "ENVIRONMENT=development" in content, "ENVIRONMENT should be development"
assert "COMPOSE_PROJECT_NAME=breakpilot-dev" in content, "Project name should be breakpilot-dev"
assert "POSTGRES_DB=breakpilot_dev" in content, "Database should be breakpilot_dev"
assert "DEBUG=true" in content, "DEBUG should be true in development"
def test_env_staging_content(self):
"""Test that .env.staging has correct environment settings."""
env_file = PROJECT_ROOT / ".env.staging"
if not env_file.exists():
pytest.skip(".env.staging not found")
content = env_file.read_text()
# Required settings for staging
assert "ENVIRONMENT=staging" in content, "ENVIRONMENT should be staging"
assert "COMPOSE_PROJECT_NAME=breakpilot-staging" in content, "Project name should be breakpilot-staging"
assert "POSTGRES_DB=breakpilot_staging" in content, "Database should be breakpilot_staging"
assert "DEBUG=false" in content, "DEBUG should be false in staging"
def test_env_files_have_different_databases(self):
"""Test that dev and staging use different database names."""
env_dev = PROJECT_ROOT / ".env.dev"
env_staging = PROJECT_ROOT / ".env.staging"
if not (env_dev.exists() and env_staging.exists()):
pytest.skip("Environment files not found")
dev_content = env_dev.read_text()
staging_content = env_staging.read_text()
# Extract database names
dev_db = None
staging_db = None
for line in dev_content.split("\n"):
if line.startswith("POSTGRES_DB="):
dev_db = line.split("=")[1].strip()
for line in staging_content.split("\n"):
if line.startswith("POSTGRES_DB="):
staging_db = line.split("=")[1].strip()
assert dev_db is not None, "POSTGRES_DB not found in .env.dev"
assert staging_db is not None, "POSTGRES_DB not found in .env.staging"
assert dev_db != staging_db, f"Dev and staging should use different databases, both use {dev_db}"
class TestDockerComposeFiles:
"""Test Docker Compose configuration files."""
def test_docker_compose_yml_exists(self):
"""Test that main docker-compose.yml exists."""
compose_file = PROJECT_ROOT / "docker-compose.yml"
assert compose_file.exists(), "docker-compose.yml should exist"
def test_docker_compose_override_exists(self):
"""Test that docker-compose.override.yml exists for dev."""
compose_file = PROJECT_ROOT / "docker-compose.override.yml"
assert compose_file.exists(), "docker-compose.override.yml should exist for development"
def test_docker_compose_staging_exists(self):
"""Test that docker-compose.staging.yml exists."""
compose_file = PROJECT_ROOT / "docker-compose.staging.yml"
assert compose_file.exists(), "docker-compose.staging.yml should exist"
def test_docker_compose_override_valid_yaml(self):
"""Test that docker-compose.override.yml has valid syntax."""
compose_file = PROJECT_ROOT / "docker-compose.override.yml"
if not compose_file.exists():
pytest.skip("docker-compose.override.yml not found")
result = subprocess.run(
["docker", "compose", "-f", "docker-compose.yml", "-f", "docker-compose.override.yml", "config"],
cwd=PROJECT_ROOT,
capture_output=True,
text=True,
)
assert result.returncode == 0, f"docker-compose.override.yml syntax error: {result.stderr}"
def test_docker_compose_staging_valid_yaml(self):
"""Test that docker-compose.staging.yml has valid syntax."""
compose_file = PROJECT_ROOT / "docker-compose.staging.yml"
if not compose_file.exists():
pytest.skip("docker-compose.staging.yml not found")
result = subprocess.run(
["docker", "compose", "-f", "docker-compose.yml", "-f", "docker-compose.staging.yml", "config"],
cwd=PROJECT_ROOT,
capture_output=True,
text=True,
)
assert result.returncode == 0, f"docker-compose.staging.yml syntax error: {result.stderr}"
class TestHelperScripts:
"""Test helper scripts for environment management."""
def test_env_switch_script_exists(self):
"""Test that env-switch.sh script exists."""
script = PROJECT_ROOT / "scripts" / "env-switch.sh"
assert script.exists(), "scripts/env-switch.sh should exist"
def test_env_switch_script_executable(self):
"""Test that env-switch.sh is executable."""
script = PROJECT_ROOT / "scripts" / "env-switch.sh"
if not script.exists():
pytest.skip("env-switch.sh not found")
assert os.access(script, os.X_OK), "env-switch.sh should be executable"
def test_start_script_exists(self):
"""Test that start.sh script exists."""
script = PROJECT_ROOT / "scripts" / "start.sh"
assert script.exists(), "scripts/start.sh should exist"
def test_start_script_executable(self):
"""Test that start.sh is executable."""
script = PROJECT_ROOT / "scripts" / "start.sh"
if not script.exists():
pytest.skip("start.sh not found")
assert os.access(script, os.X_OK), "start.sh should be executable"
def test_stop_script_exists(self):
"""Test that stop.sh script exists."""
script = PROJECT_ROOT / "scripts" / "stop.sh"
assert script.exists(), "scripts/stop.sh should exist"
def test_stop_script_executable(self):
"""Test that stop.sh is executable."""
script = PROJECT_ROOT / "scripts" / "stop.sh"
if not script.exists():
pytest.skip("stop.sh not found")
assert os.access(script, os.X_OK), "stop.sh should be executable"
def test_promote_script_exists(self):
"""Test that promote.sh script exists."""
script = PROJECT_ROOT / "scripts" / "promote.sh"
assert script.exists(), "scripts/promote.sh should exist"
def test_promote_script_executable(self):
"""Test that promote.sh is executable."""
script = PROJECT_ROOT / "scripts" / "promote.sh"
if not script.exists():
pytest.skip("promote.sh not found")
assert os.access(script, os.X_OK), "promote.sh should be executable"
def test_status_script_exists(self):
"""Test that status.sh script exists."""
script = PROJECT_ROOT / "scripts" / "status.sh"
assert script.exists(), "scripts/status.sh should exist"
def test_status_script_executable(self):
"""Test that status.sh is executable."""
script = PROJECT_ROOT / "scripts" / "status.sh"
if not script.exists():
pytest.skip("status.sh not found")
assert os.access(script, os.X_OK), "status.sh should be executable"
class TestGitIgnore:
"""Test .gitignore configuration for environment files."""
def test_gitignore_exists(self):
"""Test that .gitignore exists."""
gitignore = PROJECT_ROOT / ".gitignore"
assert gitignore.exists(), ".gitignore should exist"
def test_gitignore_excludes_env(self):
"""Test that .gitignore excludes .env file."""
gitignore = PROJECT_ROOT / ".gitignore"
if not gitignore.exists():
pytest.skip(".gitignore not found")
content = gitignore.read_text()
# Check for .env exclusion pattern
assert ".env" in content, ".gitignore should exclude .env"
def test_gitignore_includes_env_dev(self):
"""Test that .gitignore includes .env.dev (not excluded)."""
gitignore = PROJECT_ROOT / ".gitignore"
if not gitignore.exists():
pytest.skip(".gitignore not found")
content = gitignore.read_text()
# Check for .env.dev inclusion pattern (negation)
assert "!.env.dev" in content, ".gitignore should include .env.dev"
def test_gitignore_includes_env_staging(self):
"""Test that .gitignore includes .env.staging (not excluded)."""
gitignore = PROJECT_ROOT / ".gitignore"
if not gitignore.exists():
pytest.skip(".gitignore not found")
content = gitignore.read_text()
# Check for .env.staging inclusion pattern (negation)
assert "!.env.staging" in content, ".gitignore should include .env.staging"
class TestDocumentation:
"""Test that environment documentation exists."""
def test_environments_md_exists(self):
"""Test that environments.md documentation exists."""
doc = PROJECT_ROOT / "docs" / "architecture" / "environments.md"
assert doc.exists(), "docs/architecture/environments.md should exist"
def test_environment_setup_guide_exists(self):
"""Test that environment-setup.md guide exists."""
doc = PROJECT_ROOT / "docs" / "guides" / "environment-setup.md"
assert doc.exists(), "docs/guides/environment-setup.md should exist"
def test_test_environment_setup_script_exists(self):
"""Test that test-environment-setup.sh script exists."""
script = PROJECT_ROOT / "scripts" / "test-environment-setup.sh"
assert script.exists(), "scripts/test-environment-setup.sh should exist"
class TestEnvironmentIsolation:
"""Test environment isolation settings."""
@pytest.fixture
def env_configs(self) -> Dict[str, Dict[str, str]]:
"""Parse environment files into dictionaries."""
configs = {}
for env_name in ["dev", "staging"]:
env_file = PROJECT_ROOT / f".env.{env_name}"
if not env_file.exists():
continue
config = {}
for line in env_file.read_text().split("\n"):
line = line.strip()
if line and not line.startswith("#") and "=" in line:
key, value = line.split("=", 1)
config[key.strip()] = value.strip()
configs[env_name] = config
return configs
def test_unique_compose_project_names(self, env_configs):
"""Test that each environment has a unique COMPOSE_PROJECT_NAME."""
if len(env_configs) < 2:
pytest.skip("Need at least 2 environment files")
project_names = [
config.get("COMPOSE_PROJECT_NAME")
for config in env_configs.values()
if config.get("COMPOSE_PROJECT_NAME")
]
assert len(project_names) == len(set(project_names)), "COMPOSE_PROJECT_NAME should be unique per environment"
def test_unique_database_names(self, env_configs):
"""Test that each environment uses a unique database name."""
if len(env_configs) < 2:
pytest.skip("Need at least 2 environment files")
db_names = [
config.get("POSTGRES_DB")
for config in env_configs.values()
if config.get("POSTGRES_DB")
]
assert len(db_names) == len(set(db_names)), "POSTGRES_DB should be unique per environment"
def test_debug_disabled_in_staging(self, env_configs):
"""Test that DEBUG is disabled in staging environment."""
if "staging" not in env_configs:
pytest.skip(".env.staging not found")
debug_value = env_configs["staging"].get("DEBUG", "").lower()
assert debug_value == "false", "DEBUG should be false in staging"
def test_debug_enabled_in_dev(self, env_configs):
"""Test that DEBUG is enabled in dev environment."""
if "dev" not in env_configs:
pytest.skip(".env.dev not found")
debug_value = env_configs["dev"].get("DEBUG", "").lower()
assert debug_value == "true", "DEBUG should be true in development"
if __name__ == "__main__":
pytest.main([__file__, "-v"])