import pytest from unittest.mock import AsyncMock, MagicMock, patch from fabledassistant.services.tools.web import lookup_tool @pytest.mark.asyncio async def test_lookup_wikipedia_hit(): wiki_data = { "title": "QUIC", "extract": "QUIC is a transport layer protocol.", "url": "https://en.wikipedia.org/wiki/QUIC", "thumbnail_url": "", } with patch("fabledassistant.services.wikipedia.wiki_summary", new_callable=AsyncMock, return_value=wiki_data), \ patch("fabledassistant.config.Config") as mock_config: mock_config.searxng_enabled.return_value = False result = await lookup_tool(user_id=1, arguments={"query": "QUIC"}) assert result["success"] is True assert result["type"] == "lookup" assert result["data"]["wikipedia"]["title"] == "QUIC" assert result["data"]["web"] == [] assert "image" not in result["data"]["wikipedia"] @pytest.mark.asyncio async def test_lookup_wikipedia_miss_searxng_fallback(): searxng_results = [ {"url": "https://example.com/quic", "title": "QUIC Explained", "snippet": "An overview..."}, ] with patch("fabledassistant.services.wikipedia.wiki_summary", new_callable=AsyncMock, return_value=None), \ patch("fabledassistant.config.Config") as mock_config, \ patch("fabledassistant.services.research._search_searxng", new_callable=AsyncMock, return_value=searxng_results), \ patch("fabledassistant.services.article_fetcher.fetch_article_text", new_callable=AsyncMock, return_value="Full article about QUIC..."): mock_config.searxng_enabled.return_value = True result = await lookup_tool(user_id=1, arguments={"query": "QUIC"}) assert result["success"] is True assert result["data"]["wikipedia"] is None assert len(result["data"]["web"]) == 1 assert result["data"]["web"][0]["url"] == "https://example.com/quic" @pytest.mark.asyncio async def test_lookup_parallel_both_sources(): wiki_data = { "title": "President of the United States", "extract": "The president is the head of state.", "url": "https://en.wikipedia.org/wiki/President_of_the_United_States", "thumbnail_url": "", } searxng_results = [ {"url": "https://whitehouse.gov", "title": "The White House", "snippet": "Current admin..."}, ] with patch("fabledassistant.services.wikipedia.wiki_summary", new_callable=AsyncMock, return_value=wiki_data), \ patch("fabledassistant.config.Config") as mock_config, \ patch("fabledassistant.services.research._search_searxng", new_callable=AsyncMock, return_value=searxng_results), \ patch("fabledassistant.services.article_fetcher.fetch_article_text", new_callable=AsyncMock, return_value="Current president is..."): mock_config.searxng_enabled.return_value = True result = await lookup_tool(user_id=1, arguments={"query": "president of the united states"}) assert result["success"] is True assert result["data"]["wikipedia"]["title"] == "President of the United States" assert len(result["data"]["web"]) == 1 @pytest.mark.asyncio async def test_lookup_wikipedia_thumbnail_cached(): wiki_data = { "title": "Hedgehog", "extract": "Hedgehogs are spiny mammals.", "url": "https://en.wikipedia.org/wiki/Hedgehog", "thumbnail_url": "https://upload.wikimedia.org/.../Hedgehog.jpg", } mock_record = MagicMock() mock_record.id = 42 with patch("fabledassistant.services.wikipedia.wiki_summary", new_callable=AsyncMock, return_value=wiki_data), \ patch("fabledassistant.config.Config") as mock_config, \ patch("fabledassistant.services.images.fetch_and_store_image", new_callable=AsyncMock, return_value=mock_record): mock_config.searxng_enabled.return_value = False result = await lookup_tool(user_id=1, arguments={"query": "hedgehog"}) assert result["data"]["wikipedia"]["image"]["embed"] == "![Hedgehog](/api/images/42)" assert "Wikipedia" in result["data"]["wikipedia"]["image"]["citation"] assert "image_instructions" in result["data"] @pytest.mark.asyncio async def test_lookup_wikipedia_miss_no_searxng(): with patch("fabledassistant.services.wikipedia.wiki_summary", new_callable=AsyncMock, return_value=None), \ patch("fabledassistant.config.Config") as mock_config: mock_config.searxng_enabled.return_value = False result = await lookup_tool(user_id=1, arguments={"query": "xyznonexistent"}) assert result["success"] is True assert "message" in result["data"] @pytest.mark.asyncio async def test_lookup_always_available(): with patch("fabledassistant.services.tools._registry.is_caldav_configured", new_callable=AsyncMock, return_value=False), \ patch("fabledassistant.services.settings.get_setting", new_callable=AsyncMock, return_value="false"): from fabledassistant.services.tools import get_tools_for_user tools = await get_tools_for_user(user_id=1) tool_names = {t["function"]["name"] for t in tools} assert "lookup" in tool_names