74 lines
2.7 KiB
Python
74 lines
2.7 KiB
Python
import asyncio
|
|
import os
|
|
import tempfile
|
|
import unittest
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
_TEST_DATA_DIR = None
|
|
if "SOVEREIGNAI_DATA_DIR" not in os.environ:
|
|
_TEST_DATA_DIR = tempfile.TemporaryDirectory(prefix="sovereignai-web-search-tests-")
|
|
os.environ["SOVEREIGNAI_DATA_DIR"] = _TEST_DATA_DIR.name
|
|
|
|
from app.main import app
|
|
from tests.api_client import authenticated_client
|
|
|
|
|
|
class _FakeSearchClient:
|
|
async def __aenter__(self):
|
|
return self
|
|
|
|
async def __aexit__(self, exc_type, exc, traceback):
|
|
return None
|
|
|
|
async def get(self, *args, **kwargs):
|
|
return SimpleNamespace(
|
|
content=b"<html>mock search</html>",
|
|
text="<html>mock search</html>",
|
|
raise_for_status=lambda: None,
|
|
)
|
|
|
|
|
|
class WebSearchApiTests(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls) -> None:
|
|
cls.client = authenticated_client(app)
|
|
|
|
def test_search_returns_per_source_and_total_fetch_durations(self) -> None:
|
|
candidates = [
|
|
{"title": "Source A", "url": "https://a.example/article", "snippet": "A excerpt"},
|
|
{"title": "Source B", "url": "https://b.example/article", "snippet": "B excerpt"},
|
|
]
|
|
|
|
async def read_page(url: str) -> tuple[str, str, str]:
|
|
await asyncio.sleep(0.01)
|
|
title = "A article" if "a.example" in url else "B article"
|
|
return url, title, f"Content for {title}"
|
|
|
|
provider = MagicMock(default_model="test-model")
|
|
completion = {"choices": [{"message": {"content": "ملخص موثق."}}]}
|
|
with (
|
|
patch("app.main.httpx.AsyncClient", return_value=_FakeSearchClient()),
|
|
patch("app.main.parse_duckduckgo_results", return_value=candidates),
|
|
patch("app.main._validate_public_http_url", side_effect=lambda url: url),
|
|
patch("app.main._read_public_page", side_effect=read_page),
|
|
patch("app.main.get_model_provider", return_value=provider),
|
|
patch("app.main.get_completion", new=AsyncMock(return_value=completion)),
|
|
):
|
|
response = self.client.post(
|
|
"/v1/web/search",
|
|
json={"query": "اختبار البحث", "max_results": 2},
|
|
)
|
|
|
|
self.assertEqual(response.status_code, 200, response.text)
|
|
body = response.json()
|
|
self.assertEqual(body["result"], "ملخص موثق.")
|
|
self.assertGreaterEqual(body["source_fetch_ms"], 0)
|
|
self.assertEqual(len(body["sources"]), 2)
|
|
self.assertTrue(all(source["fetch_ms"] >= 0 for source in body["sources"]))
|
|
self.assertTrue(all(source["status"] == "read" for source in body["sources"]))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|