import os from unittest.mock import MagicMock import pytest from app.services.ai_engine.base import AIEngineAdapter, EngineType from app.services.api_key_manager import APIKeyManager, KeySource class MockAdapter(AIEngineAdapter): def __init__(self, api_key: str | None = None, **kwargs): super().__init__(api_key=api_key, **kwargs) async def query(self, query: str, brand_name: str, competitor_names: list[str] | None = None): pass def get_engine_type(self) -> EngineType: return EngineType.CHATGPT def _get_env_key(self) -> str | None: return os.getenv("OPENAI_API_KEY", "") class TestAdapterKeySource: def test_adapter_accepts_key_manager_parameter(self): key_manager = MagicMock(spec=APIKeyManager) adapter = MockAdapter(key_manager=key_manager, user_id="user123") assert adapter._key_manager is key_manager assert adapter._user_id == "user123" def test_adapter_accepts_api_key_parameter(self): adapter = MockAdapter(api_key="direct-key-123") assert adapter.api_key == "direct-key-123" def test_resolve_key_from_direct_api_key(self): adapter = MockAdapter(api_key="direct-key-123") assert adapter.api_key == "direct-key-123" def test_resolve_key_from_key_manager_user_key(self): key_manager = MagicMock(spec=APIKeyManager) key_manager.get_key.return_value = "manager-key-456" adapter = MockAdapter(key_manager=key_manager, user_id="user123") key_manager.get_key.assert_called_once_with("chatgpt", user_id="user123") assert adapter.api_key == "manager-key-456" def test_resolve_key_fallback_to_env_when_no_manager(self, monkeypatch): monkeypatch.setenv("OPENAI_API_KEY", "env-key-789") adapter = MockAdapter() assert adapter.api_key == "env-key-789" def test_direct_api_key_priority_over_key_manager(self): key_manager = MagicMock(spec=APIKeyManager) key_manager.get_key.return_value = "manager-key-456" adapter = MockAdapter(api_key="direct-key-123", key_manager=key_manager, user_id="user123") key_manager.get_key.assert_not_called() assert adapter.api_key == "direct-key-123" def test_user_key_priority_over_system_key(self): key_manager = MagicMock(spec=APIKeyManager) key_manager.get_key.return_value = "user-key-from-manager" adapter = MockAdapter(key_manager=key_manager, user_id="user123") assert adapter.api_key == "user-key-from-manager" def test_no_key_available_returns_empty(self, monkeypatch): key_manager = MagicMock(spec=APIKeyManager) key_manager.get_key.return_value = None # 清除环境变量,确保不会从环境变量获取key monkeypatch.delenv("OPENAI_API_KEY", raising=False) adapter = MockAdapter(key_manager=key_manager, user_id="user123") assert adapter.api_key == "" def test_adapter_with_all_parameters(self): key_manager = MagicMock(spec=APIKeyManager) key_manager.get_key.return_value = "full-key" adapter = MockAdapter( api_key=None, rate_limiter=MagicMock(), proxy="http://proxy:8080", key_manager=key_manager, user_id="test-user" ) assert adapter._key_manager is key_manager assert adapter._user_id == "test-user" assert adapter.proxy == "http://proxy:8080" class TestBatchQueryServiceKeyIntegration: def test_build_adapters_with_key_manager(self): from app.services.ai_engine.batch_query import BatchQueryService key_manager = MagicMock(spec=APIKeyManager) key_manager.get_key.return_value = "batch-test-key" adapters = { "chatgpt": MockAdapter(key_manager=key_manager, user_id="batch-user") } service = BatchQueryService(adapters) service.set_user_context(user_id="batch-user", brand_id="brand-123") assert service._user_id == "batch-user" assert service._brand_id == "brand-123"