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"mock search", text="mock search", 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()