Files
group_fqcd_jr/tests/unit/service/test_model_gateway.py
T

76 lines
2.9 KiB
Python
Raw Normal View History

2026-09-09 21:55:37 +08:00
from types import SimpleNamespace
import httpx
import pytest
from app.core.errors import RecoverableAgentError
from app.service.model_gateway import (
ModelDispatchService,
ModelGenerationService,
OpenAICompatibleGateway,
)
class Gateway:
def __init__(self) -> None:
self.calls: list[str] = []
async def generate(self, *, endpoint_code: str, prompt: str, timeout_ms: int) -> str:
self.calls.append(endpoint_code)
if endpoint_code == "primary":
raise TimeoutError("timeout")
return "fallback answer"
class Endpoint:
def __init__(self, code: str) -> None:
self.endpoint_code = code
self.timeout_ms = 1000
@pytest.mark.asyncio
async def test_model_dispatch_uses_declared_fallback_order() -> None:
gateway = Gateway()
result = await ModelDispatchService(gateway).generate(
[Endpoint("primary"), Endpoint("fallback")], "hello"
)
assert result.text == "fallback answer"
assert result.degraded is True
assert gateway.calls == ["primary", "fallback"]
@pytest.mark.asyncio
async def test_openai_compatible_gateway_uses_secret_ref_and_parses_text(monkeypatch) -> None:
monkeypatch.setenv("TEST_MODEL_KEY", "secret-value")
requests: list[httpx.Request] = []
async def handler(request: httpx.Request) -> httpx.Response:
requests.append(request)
return httpx.Response(200, json={"choices": [{"message": {"content": "answer"}}]})
client = httpx.AsyncClient(transport=httpx.MockTransport(handler))
endpoint = SimpleNamespace(endpoint_code="primary", base_url="https://model.test/v1",
model_name="test-model", secret_ref="env:TEST_MODEL_KEY")
gateway = OpenAICompatibleGateway({"primary": endpoint}, client=client)
try:
assert await gateway.generate(
endpoint_code="primary", prompt="hello", timeout_ms=1000
) == "answer"
assert str(requests[0].url) == "https://model.test/v1/chat/completions"
assert requests[0].headers["authorization"] == "Bearer secret-value"
assert requests[0].content.find(b"secret-value") == -1
finally:
await client.aclose()
@pytest.mark.asyncio
async def test_gateway_missing_secret_and_empty_route_fail_closed(monkeypatch) -> None:
monkeypatch.delenv("MISSING_MODEL_KEY", raising=False)
endpoint = SimpleNamespace(endpoint_code="primary", base_url="https://model.test",
model_name="test-model", secret_ref="env:MISSING_MODEL_KEY")
with pytest.raises(RecoverableAgentError, match="密钥未配置"):
await OpenAICompatibleGateway({"primary": endpoint}).generate(
endpoint_code="primary", prompt="hello", timeout_ms=1000)
with pytest.raises(RecoverableAgentError, match="没有可用"):
await ModelGenerationService(ModelDispatchService(Gateway())).generate([], "hello")