"""回归测试: 生产环境管理接口鉴权 fail-closed。 验证: 1. REQUIRE_ADMIN_AUTH=True + 未配置 → 拒绝(503) 2. APP_ENV=production + 未配置 → 拒绝(503) 3. development + 未配置 → 放行(fail-open + warning) 4. 已配置密码 → 正常验证路径不受影响 """ from __future__ import annotations from unittest.mock import AsyncMock, patch import pytest from fastapi import HTTPException from src.api.deps import require_admin from src.core.config import Settings class TestRequireAdminFailClosed: """生产环境 fail-closed 逻辑。""" @pytest.mark.asyncio async def test_require_admin_auth_true_rejects_when_unconfigured(self): """REQUIRE_ADMIN_AUTH=True + 未配置 → 503 拒绝。""" mock_request = AsyncMock() mock_request.cookies = {} mock_request.headers = {} with patch("src.api.deps.settings", Settings(REQUIRE_ADMIN_AUTH=True, APP_ENV="development")), \ patch("src.api.deps.auth_configured", AsyncMock(return_value=False)): with pytest.raises(HTTPException) as exc_info: await require_admin(mock_request) assert exc_info.value.status_code == 503 assert "未配置" in exc_info.value.detail or "鉴权" in exc_info.value.detail @pytest.mark.asyncio async def test_production_env_rejects_when_unconfigured(self): """APP_ENV=production + 未配置 → 503 拒绝。""" mock_request = AsyncMock() mock_request.cookies = {} mock_request.headers = {} with patch("src.api.deps.settings", Settings(REQUIRE_ADMIN_AUTH=False, APP_ENV="production")), \ patch("src.api.deps.auth_configured", AsyncMock(return_value=False)): with pytest.raises(HTTPException) as exc_info: await require_admin(mock_request) assert exc_info.value.status_code == 503 @pytest.mark.asyncio async def test_development_env_allows_when_unconfigured(self): """development + 未配置 → fail-open 放行。""" mock_request = AsyncMock() mock_request.cookies = {} mock_request.headers = {} with patch("src.api.deps.settings", Settings(REQUIRE_ADMIN_AUTH=False, APP_ENV="development")), \ patch("src.api.deps.auth_configured", AsyncMock(return_value=False)): # 不应抛异常 await require_admin(mock_request) @pytest.mark.asyncio async def test_configured_password_works_normally(self): """已配置密码 → 正常验证路径(401 未登录,而非 503)。""" mock_request = AsyncMock() mock_request.cookies = {} # 无 cookie mock_request.headers = {} # 无 API Key with patch("src.api.deps.settings", Settings(REQUIRE_ADMIN_AUTH=True, APP_ENV="production")), \ patch("src.api.deps.auth_configured", AsyncMock(return_value=True)), \ patch("src.api.deps.get_session_secret", AsyncMock(return_value=b"secret")): with pytest.raises(HTTPException) as exc_info: await require_admin(mock_request) # 已配置 → 401(未登录),不是 503(未配置) assert exc_info.value.status_code == 401