Files
office_translator/tests/test_config_env.py
Sepehr Ramezani 26bd096a06 feat: production deployment - full update with providers, admin, glossaries, pricing, tests
Major changes across backend, frontend, infrastructure:
- Provider system with model selection (Google, DeepL, OpenAI, Ollama, Google Cloud)
- Admin panel: user management, pricing, settings
- Glossary system with CSV import/export
- Subscription and tier quota management
- Security hardening (rate limiting, API key auth, path traversal fixes)
- Docker compose for dev, prod, and IONOS deployment
- Alembic migrations for new tables
- Frontend: dashboard, pricing page, landing page, i18n (en/fr)
- Test suite and verification scripts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-25 15:01:47 +02:00

138 lines
5.2 KiB
Python

"""
Tests for Story 6.6: Environment configuration and fail-fast validation (NFR10).
- With ENV=development (or unset): no fail-fast, validate_required_env returns [].
- With ENV=production and missing required vars: validate_required_env returns list of missing names.
- Startup exit message format (tested via validation output; full main import may lack deps in test env).
"""
import os
import sys
import subprocess
import tempfile
# Project root for cwd and PYTHONPATH
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def test_validate_required_env_development_returns_empty():
"""In development, no required vars are enforced (defaults/warnings allowed)."""
env = os.environ.copy()
env.pop("ENV", None)
env.pop("ENVIRONMENT", None)
env["ENV"] = "development"
result = subprocess.run(
[sys.executable, "-c", "from config import config; print(repr(config.validate_required_env()))"],
capture_output=True,
text=True,
cwd=ROOT,
env=env,
)
assert result.returncode == 0
out = (result.stdout or "").strip()
assert out == "[]", f"Expected [] in development, got {out}"
def test_validate_required_env_production_reports_missing():
"""In production with no .env loaded, all required vars are reported missing."""
with tempfile.TemporaryDirectory() as tmp:
env = {
"ENV": "production",
"PATH": os.environ.get("PATH", ""),
"PYTHONPATH": ROOT,
}
# Run from empty dir so load_dotenv() does not load project .env
result = subprocess.run(
[
sys.executable,
"-c",
"from config import config; m = config.validate_required_env(); print(','.join(sorted(m)))",
],
capture_output=True,
text=True,
cwd=tmp,
env=env,
)
assert result.returncode == 0
out = (result.stdout or "").strip()
required_names = {"ADMIN_PASSWORD or ADMIN_PASSWORD_HASH", "ADMIN_TOKEN_SECRET", "ADMIN_USERNAME", "DATABASE_URL", "JWT_SECRET_KEY", "REDIS_URL"}
reported = set(out.split(",")) if out else set()
assert required_names == reported, f"Expected {required_names}, got {reported}"
def test_fail_fast_message_lists_vars_and_env_example():
"""With ENV=production and some vars missing, message lists them and mentions .env.example."""
with tempfile.TemporaryDirectory() as tmp:
env = {"ENV": "production", "PATH": os.environ.get("PATH", ""), "PYTHONPATH": ROOT}
result = subprocess.run(
[
sys.executable,
"-c",
"from config import config; m = config.validate_required_env(); "
"msg = 'Missing required env: ' + ', '.join(m) + '. Set them in .env or environment. See .env.example.' if m else ''; "
"print(msg)",
],
capture_output=True,
text=True,
cwd=tmp,
env=env,
)
assert result.returncode == 0
out = (result.stdout or "").strip()
assert "Missing required env" in out
assert ".env.example" in out
assert "JWT_SECRET_KEY" in out or "DATABASE_URL" in out or "REDIS_URL" in out
def test_validate_required_env_production_postgres_star_satisfies_database_url():
"""When POSTGRES_* are set (and DATABASE_URL is not), DATABASE_URL is not reported missing (AC #1)."""
with tempfile.TemporaryDirectory() as tmp:
env = {
"ENV": "production",
"PATH": os.environ.get("PATH", ""),
"PYTHONPATH": ROOT,
"POSTGRES_HOST": "localhost",
"POSTGRES_PORT": "5432",
"POSTGRES_USER": "u",
"POSTGRES_PASSWORD": "p",
"POSTGRES_DB": "db",
"JWT_SECRET_KEY": "x",
"ADMIN_USERNAME": "a",
"ADMIN_PASSWORD": "b",
"ADMIN_TOKEN_SECRET": "c",
"RATE_LIMIT_ENABLED": "false",
}
result = subprocess.run(
[
sys.executable,
"-c",
"from config import config; m = config.validate_required_env(); print(','.join(sorted(m)))",
],
capture_output=True,
text=True,
cwd=tmp,
env=env,
)
assert result.returncode == 0
out = (result.stdout or "").strip()
assert out == "", f"Expected no missing vars when POSTGRES_* set, got {out}"
def test_startup_exits_with_code_1_when_production_missing_required_env():
"""App startup must exit with code 1 when ENV=production and required vars are missing (Story 6.6)."""
with tempfile.TemporaryDirectory() as tmp:
env = {
"ENV": "production",
"PATH": os.environ.get("PATH", ""),
"PYTHONPATH": ROOT,
}
result = subprocess.run(
[sys.executable, "-c", "import main"],
capture_output=True,
text=True,
cwd=tmp,
env=env,
)
assert result.returncode == 1, f"Expected exit 1, got {result.returncode}. stderr: {result.stderr}"
assert "Missing required env" in (result.stderr or "")
assert ".env.example" in (result.stderr or "")