import importlib import pytest from fastapi.testclient import TestClient def _load_app_main(monkeypatch, *, disable_startup_tasks="1"): monkeypatch.setenv("APP_ENV", "test") monkeypatch.setenv("DISABLE_STARTUP_TASKS", disable_startup_tasks) monkeypatch.setenv("DB_HOST", "localhost") monkeypatch.setenv("DB_NAME", "trading_db") monkeypatch.setenv("DB_USER", "trader") monkeypatch.setenv("DB_PASSWORD", "test-password") monkeypatch.setenv("CORS_ORIGINS", "http://localhost:3000") monkeypatch.setenv("BROKER_TOKEN_KEY", "test-broker-token-key") monkeypatch.setenv("RESET_OTP_SECRET", "test-reset-secret") import app.main as app_main importlib.reload(app_main) return app_main def test_liveness_succeeds_even_if_db_is_unavailable(monkeypatch): app_main = _load_app_main(monkeypatch) app = app_main.create_app() import app.routers.health as health_router monkeypatch.setattr(health_router, "health_check", lambda: False) with TestClient(app) as client: response = client.get("/health/live") assert response.status_code == 200 assert response.json() == {"status": "ok"} def test_readiness_fails_when_db_is_unavailable(monkeypatch): app_main = _load_app_main(monkeypatch) app = app_main.create_app() import app.routers.health as health_router monkeypatch.setattr(health_router, "health_check", lambda: False) with TestClient(app) as client: response = client.get("/health/ready") assert response.status_code == 503 assert response.json()["detail"]["db"] == "unavailable" def test_readiness_succeeds_when_startup_complete_and_db_available(monkeypatch): app_main = _load_app_main(monkeypatch) app = app_main.create_app() import app.routers.health as health_router monkeypatch.setattr(health_router, "health_check", lambda: True) with TestClient(app) as client: response = client.get("/health/ready") assert response.status_code == 200 assert response.json()["status"] == "ok" assert response.json()["startup"]["complete"] is True def test_startup_failure_keeps_readiness_unhealthy(monkeypatch): app_main = _load_app_main(monkeypatch, disable_startup_tasks="0") app = app_main.create_app() import app.routers.health as health_router monkeypatch.setattr(health_router, "health_check", lambda: True) def fake_startup_tasks(app): app.state.startup_started = True app.state.startup_complete = False app.state.startup_error = "resume_failed" monkeypatch.setattr(app_main, "_run_startup_tasks", fake_startup_tasks) with TestClient(app) as client: response = client.get("/health") assert response.status_code == 503 assert response.json()["detail"]["startup"]["error"] == "resume_failed" def test_production_boot_fails_without_broker_token_key(monkeypatch): monkeypatch.setenv("APP_ENV", "production") monkeypatch.setenv("DB_HOST", "localhost") monkeypatch.setenv("DB_NAME", "trading_db") monkeypatch.setenv("DB_USER", "trader") monkeypatch.setenv("DB_PASSWORD", "test-password") monkeypatch.setenv("CORS_ORIGINS", "https://app.quantfortune.com") monkeypatch.setenv("RESET_OTP_SECRET", "test-reset-secret") monkeypatch.delenv("BROKER_TOKEN_KEY", raising=False) import app.main as app_main with pytest.raises(RuntimeError, match="BROKER_TOKEN_KEY must be configured in production"): importlib.reload(app_main) def test_production_bootstrap_requires_explicit_admin_credentials(monkeypatch): monkeypatch.setenv("APP_ENV", "production") monkeypatch.setenv("DB_HOST", "localhost") monkeypatch.setenv("DB_NAME", "trading_db") monkeypatch.setenv("DB_USER", "trader") monkeypatch.setenv("DB_PASSWORD", "test-password") monkeypatch.setenv("CORS_ORIGINS", "https://app.quantfortune.com") monkeypatch.setenv("RESET_OTP_SECRET", "test-reset-secret") monkeypatch.setenv("BROKER_TOKEN_KEY", "test-broker-token-key") monkeypatch.setenv("ENABLE_SUPER_ADMIN_BOOTSTRAP", "1") monkeypatch.delenv("SUPER_ADMIN_EMAIL", raising=False) monkeypatch.delenv("SUPER_ADMIN_PASSWORD", raising=False) import app.main as app_main with pytest.raises(RuntimeError, match="SUPER_ADMIN_EMAIL must be configured when bootstrap is enabled"): importlib.reload(app_main)