"""Tests for the Wikipedia service module.""" import pytest from unittest.mock import AsyncMock, MagicMock, patch from fabledassistant.services.wikipedia import wiki_summary, wiki_search def _make_mock_response(json_data: dict, status_code: int = 200) -> MagicMock: mock_resp = MagicMock() mock_resp.status_code = status_code mock_resp.json.return_value = json_data mock_resp.raise_for_status = MagicMock() return mock_resp @pytest.mark.asyncio async def test_wiki_summary_returns_extract(): """Successful lookup returns a dict with title, extract, and url.""" payload = { "type": "standard", "title": "Python (programming language)", "extract": "Python is a high-level programming language.", "content_urls": {"desktop": {"page": "https://en.wikipedia.org/wiki/Python_(programming_language)"}}, } mock_resp = _make_mock_response(payload) with patch("fabledassistant.services.wikipedia.httpx.AsyncClient") as mock_client_cls: mock_client = AsyncMock() mock_client.__aenter__ = AsyncMock(return_value=mock_client) mock_client.__aexit__ = AsyncMock(return_value=False) mock_client.get = AsyncMock(return_value=mock_resp) mock_client_cls.return_value = mock_client result = await wiki_summary("Python programming language") assert result is not None assert result["title"] == "Python (programming language)" assert "Python" in result["extract"] assert result["url"].startswith("https://") assert result["thumbnail_url"] == "" @pytest.mark.asyncio async def test_wiki_summary_includes_thumbnail(): """Thumbnail URL is extracted when the article has an image.""" payload = { "type": "standard", "title": "Hedgehog", "extract": "Hedgehogs are spiny mammals.", "content_urls": {"desktop": {"page": "https://en.wikipedia.org/wiki/Hedgehog"}}, "thumbnail": {"source": "https://upload.wikimedia.org/foo-320px.jpg"}, "originalimage": {"source": "https://upload.wikimedia.org/foo.jpg"}, } mock_resp = _make_mock_response(payload) with patch("fabledassistant.services.wikipedia.httpx.AsyncClient") as mock_client_cls: mock_client = AsyncMock() mock_client.__aenter__ = AsyncMock(return_value=mock_client) mock_client.__aexit__ = AsyncMock(return_value=False) mock_client.get = AsyncMock(return_value=mock_resp) mock_client_cls.return_value = mock_client result = await wiki_summary("Hedgehog") assert result is not None assert result["thumbnail_url"] == "https://upload.wikimedia.org/foo-320px.jpg" @pytest.mark.asyncio async def test_wiki_summary_returns_none_on_404(): """HTTP error (e.g. 404) causes wiki_summary to return None.""" import httpx mock_resp = MagicMock() mock_resp.raise_for_status.side_effect = httpx.HTTPStatusError( "404", request=MagicMock(), response=MagicMock() ) with patch("fabledassistant.services.wikipedia.httpx.AsyncClient") as mock_client_cls: mock_client = AsyncMock() mock_client.__aenter__ = AsyncMock(return_value=mock_client) mock_client.__aexit__ = AsyncMock(return_value=False) mock_client.get = AsyncMock(return_value=mock_resp) mock_client_cls.return_value = mock_client result = await wiki_summary("NonExistentPageXYZ123") assert result is None @pytest.mark.asyncio async def test_wiki_summary_returns_none_on_disambiguation(): """Disambiguation pages cause wiki_summary to return None.""" payload = { "type": "disambiguation", "title": "Mercury", "extract": "Mercury may refer to:", "content_urls": {"desktop": {"page": "https://en.wikipedia.org/wiki/Mercury"}}, } mock_resp = _make_mock_response(payload) with patch("fabledassistant.services.wikipedia.httpx.AsyncClient") as mock_client_cls: mock_client = AsyncMock() mock_client.__aenter__ = AsyncMock(return_value=mock_client) mock_client.__aexit__ = AsyncMock(return_value=False) mock_client.get = AsyncMock(return_value=mock_resp) mock_client_cls.return_value = mock_client result = await wiki_summary("Mercury") assert result is None @pytest.mark.asyncio async def test_wiki_search_returns_results(): """wiki_search returns a list of result dicts for a successful query.""" search_payload = { "query": { "search": [ {"title": "Quantum mechanics"}, {"title": "Quantum field theory"}, ] } } summary_payloads = [ { "type": "standard", "title": "Quantum mechanics", "extract": "Quantum mechanics is a fundamental theory in physics.", "content_urls": {"desktop": {"page": "https://en.wikipedia.org/wiki/Quantum_mechanics"}}, }, { "type": "standard", "title": "Quantum field theory", "extract": "Quantum field theory is a framework in theoretical physics.", "content_urls": {"desktop": {"page": "https://en.wikipedia.org/wiki/Quantum_field_theory"}}, }, ] search_resp = _make_mock_response(search_payload) summary_resp_1 = _make_mock_response(summary_payloads[0]) summary_resp_2 = _make_mock_response(summary_payloads[1]) with patch("fabledassistant.services.wikipedia.httpx.AsyncClient") as mock_client_cls: mock_client = AsyncMock() mock_client.__aenter__ = AsyncMock(return_value=mock_client) mock_client.__aexit__ = AsyncMock(return_value=False) mock_client.get = AsyncMock(side_effect=[search_resp, summary_resp_1, summary_resp_2]) mock_client_cls.return_value = mock_client results = await wiki_search("quantum physics", limit=2) assert len(results) == 2 assert results[0]["title"] == "Quantum mechanics" assert "quantum" in results[0]["extract"].lower() assert results[1]["title"] == "Quantum field theory" @pytest.mark.asyncio async def test_wiki_search_returns_empty_on_failure(): """wiki_search returns an empty list when the network request fails.""" import httpx with patch("fabledassistant.services.wikipedia.httpx.AsyncClient") as mock_client_cls: mock_client = AsyncMock() mock_client.__aenter__ = AsyncMock(return_value=mock_client) mock_client.__aexit__ = AsyncMock(return_value=False) mock_client.get = AsyncMock(side_effect=httpx.ConnectError("connection refused")) mock_client_cls.return_value = mock_client results = await wiki_search("anything") assert results == []