"""OpenRouter 어댑터와 provider 인증 헤더 판정 테스트.""" import os import unittest from unittest.mock import AsyncMock, patch from app.contracts.engine_gateway import GenerateRequest, EngineMessage from engine_gateway import provider_registry class _FakeResponse: def __init__(self, payload, status_code=200): self._payload = payload self.status_code = status_code def raise_for_status(self): return None def json(self): return self._payload class _FakeAsyncClient: def __init__(self, payload): self._payload = payload self.captured = {} async def __aenter__(self): return self async def __aexit__(self, *exc): return None async def get(self, url, **kwargs): self.captured = {"method": "get", "url": url, **kwargs} return _FakeResponse(self._payload) async def post(self, url, **kwargs): self.captured = {"method": "post", "url": url, **kwargs} return _FakeResponse(self._payload) class AnthropicHeaderTest(unittest.TestCase): def tearDown(self): provider_registry._PROVIDER_AUTH_KINDS.clear() def test_api_key_uses_x_api_key(self): headers = provider_registry._anthropic_headers("sk-ant-api03-x") self.assertEqual(headers["x-api-key"], "sk-ant-api03-x") self.assertNotIn("Authorization", headers) def test_oauth_token_uses_bearer(self): provider_registry.set_provider_auth_kind("claude", "oauth_token") headers = provider_registry._anthropic_headers("oauth-token") self.assertEqual(headers["Authorization"], "Bearer oauth-token") self.assertNotIn("x-api-key", headers) class DiscoverOpenRouterTest(unittest.IsolatedAsyncioTestCase): async def test_live_models_without_allowlist(self): fake = _FakeAsyncClient( { "data": [ { "id": "anthropic/claude-sonnet-4.5", "name": "Claude Sonnet 4.5", "pricing": {"prompt": "0.000003", "completion": "0.000015"}, }, { "id": "meta-llama/free-8b", "name": "Free 8B", "pricing": {"prompt": "0", "completion": "0"}, }, ] } ) with patch.dict(os.environ, {"OPENROUTER_API_KEY": "sk-or-key"}, clear=False): with patch.object(provider_registry.httpx, "AsyncClient", return_value=fake): result = await provider_registry._discover_openrouter() self.assertTrue(result.available) self.assertEqual(result.provider, "openrouter") self.assertEqual(result.source, "live_api") self.assertEqual(result.default_model, "anthropic/claude-sonnet-4.5") self.assertIn("Bearer sk-or-key", fake.captured["headers"]["Authorization"]) free = next(m for m in result.models if m.id == "meta-llama/free-8b") self.assertIn("무료", free.description) async def test_missing_key_is_unavailable(self): with patch.dict(os.environ, {"OPENROUTER_API_KEY": ""}, clear=False): result = await provider_registry._discover_openrouter() self.assertFalse(result.available) async def test_allowlist_must_use_vendor_slash_model(self): with patch.dict( os.environ, {"OPENROUTER_API_KEY": "sk-or-key", "OPENROUTER_ENGINE_MODELS": "gpt-4o"}, clear=False, ): result = await provider_registry._discover_openrouter() self.assertFalse(result.available) self.assertIn("vendor/model", result.detail) class GenerateOpenRouterTest(unittest.IsolatedAsyncioTestCase): async def test_chat_completions_roundtrip(self): fake = _FakeAsyncClient( { "model": "anthropic/claude-sonnet-4.5", "choices": [ {"message": {"role": "assistant", "content": "안녕하세요."}} ], "usage": {"prompt_tokens": 12, "completion_tokens": 5}, } ) req = GenerateRequest( ai_role="client", messages=[EngineMessage(role="user", content="hi")], provider="openrouter", model="anthropic/claude-sonnet-4.5", ) with patch.dict(os.environ, {"OPENROUTER_API_KEY": "sk-or-key"}, clear=False): with patch.object(provider_registry.httpx, "AsyncClient", return_value=fake): with patch.object( provider_registry, "_resolve_selection", AsyncMock(return_value=("anthropic/claude-sonnet-4.5", None)), ): result = await provider_registry.generate_with_provider( req, system_prompt="sys", user_payload="사용자 입력" ) self.assertEqual(result.provider, "openrouter") self.assertEqual(result.text, "안녕하세요.") self.assertEqual(result.tokens_in, 12) self.assertEqual(result.tokens_out, 5) self.assertTrue(fake.captured["url"].endswith("/chat/completions")) payload = fake.captured["json"] self.assertEqual(payload["messages"][0]["role"], "system") self.assertEqual(payload["messages"][1]["content"], "사용자 입력") if __name__ == "__main__": unittest.main()