Files
QMDSearch/tests/test_ollama_client.py
kplam 51dc8dc4f6 Initial commit: QMDSearch 分层信息检索服务
- FastAPI + Qdrant + Redis + Ollama 技术栈
- L1→L2→L3→chunk 四层分层检索(dense + sparse RRF 融合)
- 文档三级总结与 2.5 级回退
- query 解析路由与分类
- /admin 管理页面
2026-07-29 21:24:40 +08:00

127 lines
4.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""OllamaClient 单元测试(mock httpx,不发起真实网络请求)"""
from typing import Any
import httpx
import pytest
from app.services.ollama import OllamaClient
def _make_response(status_code: int, data: dict | None = None) -> httpx.Response:
"""构造带 request 上下文的 httpx.Responseraise_for_status 依赖 request"""
request = httpx.Request("POST", "http://localhost:11434/api/generate")
if data is None:
return httpx.Response(status_code, request=request)
return httpx.Response(status_code, json=data, request=request)
class _FakeAsyncClient:
"""httpx.AsyncClient 替代品:记录请求参数,返回预设响应或抛出预设异常"""
def __init__(
self,
captured: dict,
response: httpx.Response | None = None,
error: Exception | None = None,
**kwargs: Any,
) -> None:
self._captured = captured
self._response = response
self._error = error
captured["timeout"] = kwargs.get("timeout")
async def __aenter__(self) -> "_FakeAsyncClient":
return self
async def __aexit__(self, *args: object) -> bool:
return False
async def post(self, url: str, json: dict | None = None) -> httpx.Response:
self._captured["url"] = url
self._captured["json"] = json
if self._error is not None:
raise self._error
assert self._response is not None
return self._response
async def get(self, url: str) -> httpx.Response:
self._captured["url"] = url
if self._error is not None:
raise self._error
assert self._response is not None
return self._response
class TestGenerate:
async def test_returns_response_field(self, monkeypatch: pytest.MonkeyPatch) -> None:
captured: dict = {}
monkeypatch.setattr(
httpx,
"AsyncClient",
lambda **kw: _FakeAsyncClient(captured, response=_make_response(200, {"response": "生成结果"}), **kw),
)
client = OllamaClient(base_url="http://localhost:11434/", model="qwen2.5:1.5b")
result = await client.generate("你好")
assert result == "生成结果"
# base_url 尾部斜杠被去除
assert captured["url"] == "http://localhost:11434/api/generate"
assert captured["json"] == {"model": "qwen2.5:1.5b", "prompt": "你好", "stream": False}
assert "format" not in captured["json"]
assert captured["timeout"] == 120.0
async def test_json_mode_adds_format(self, monkeypatch: pytest.MonkeyPatch) -> None:
captured: dict = {}
monkeypatch.setattr(
httpx,
"AsyncClient",
lambda **kw: _FakeAsyncClient(captured, response=_make_response(200, {"response": "{}"}), **kw),
)
client = OllamaClient(base_url="http://localhost:11434", model="qwen2.5:1.5b")
await client.generate("你好", json_mode=True)
assert captured["json"]["format"] == "json"
async def test_http_error_status_raises(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
httpx, "AsyncClient", lambda **kw: _FakeAsyncClient({}, response=_make_response(500), **kw)
)
client = OllamaClient(base_url="http://localhost:11434", model="qwen2.5:1.5b")
with pytest.raises(httpx.HTTPStatusError):
await client.generate("你好")
class TestIsAvailable:
async def test_200_returns_true(self, monkeypatch: pytest.MonkeyPatch) -> None:
captured: dict = {}
monkeypatch.setattr(
httpx,
"AsyncClient",
lambda **kw: _FakeAsyncClient(captured, response=_make_response(200, {"models": []}), **kw),
)
client = OllamaClient(base_url="http://localhost:11434", model="qwen2.5:1.5b")
assert await client.is_available() is True
assert captured["url"] == "http://localhost:11434/api/tags"
assert captured["timeout"] == 5.0
async def test_non_200_returns_false(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
httpx, "AsyncClient", lambda **kw: _FakeAsyncClient({}, response=_make_response(503), **kw)
)
client = OllamaClient(base_url="http://localhost:11434", model="qwen2.5:1.5b")
assert await client.is_available() is False
async def test_connect_error_returns_false(self, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(
httpx, "AsyncClient", lambda **kw: _FakeAsyncClient({}, error=httpx.ConnectError("连接被拒绝"), **kw)
)
client = OllamaClient(base_url="http://localhost:11434", model="qwen2.5:1.5b")
assert await client.is_available() is False