import pytest import serp from serp import DuckDuckGoBlockedException, query_ddg_search class _FakeDDGS: def __init__(self, results): self._results = results def text(self, q, max_results=10): return iter(self._results) async def test_maps_duckduckgo_results_to_serp_dicts(monkeypatch): monkeypatch.setattr( serp, "DDGS", lambda: _FakeDDGS( [ {"title": "A", "body": "Body A", "href": "https://a.example"}, {"title": "B", "body": "Body B", "href": "https://b.example"}, ] ), ) results = await query_ddg_search("test query", 10) assert results == [ {"title": "A", "body": "Body A", "href": "https://a.example"}, {"title": "B", "body": "Body B", "href": "https://b.example"}, ] async def test_raises_blocked_exception_on_empty_results(monkeypatch): """Pins the fix from commit d341a7a: duckduckgo_search doesn't always raise when it's rate-limited/blocked - it can just parse zero results from the anti-bot page. An empty list must be treated as a failure so the `/serp/search` fallback chain moves on to the next backend. """ monkeypatch.setattr(serp, "DDGS", lambda: _FakeDDGS([])) with pytest.raises(DuckDuckGoBlockedException): await query_ddg_search("test query", 10)