# tests/test_fetch_server.py import pytest from unittest.mock import AsyncMock, patch, MagicMock from mcp.types import TextContent import httpx @pytest.mark.asyncio class TestFetchServer: async def test_list_tools(self, fetch_session): """Test that server lists expected tools""" response = await fetch_session.list_tools() tool_names = [tool.name for tool in response.tools] assert "fetch_url" in tool_names async def test_fetch_url_basic(self, fetch_session): """Test basic URL fetching""" with patch('httpx.AsyncClient') as mock_client: mock_response = MagicMock() mock_response.text = """

Test Article

This is a test article about ALS research.

""" mock_client_instance = AsyncMock() mock_client_instance.get = AsyncMock(return_value=mock_response) mock_client.return_value.__aenter__.return_value = mock_client_instance result = await fetch_session.call_tool( "fetch_url", {"url": "https://example.com/article", "extract_text_only": True} ) text = result.content[0].text assert "Test Article" in text assert "ALS research" in text # Should NOT contain HTML tags assert "" not in text assert "

" not in text async def test_fetch_url_raw_html(self, fetch_session): """Test fetching raw HTML""" with patch('httpx.AsyncClient') as mock_client: html_content = "

Title

" mock_response = MagicMock() mock_response.text = html_content mock_client_instance = AsyncMock() mock_client_instance.get = AsyncMock(return_value=mock_response) mock_client.return_value.__aenter__.return_value = mock_client_instance result = await fetch_session.call_tool( "fetch_url", {"url": "https://example.com", "extract_text_only": False} ) text = result.content[0].text assert "" in text assert "" in text async def test_fetch_url_strips_scripts(self, fetch_session): """Test that scripts and styles are removed""" with patch('httpx.AsyncClient') as mock_client: mock_response = MagicMock() mock_response.text = """

Content

""" mock_client_instance = AsyncMock() mock_client_instance.get = AsyncMock(return_value=mock_response) mock_client.return_value.__aenter__.return_value = mock_client_instance result = await fetch_session.call_tool( "fetch_url", {"url": "https://example.com", "extract_text_only": True} ) text = result.content[0].text assert "Content" in text assert "alert" not in text assert ".test" not in text async def test_fetch_url_follows_redirects(self, fetch_session): """Test that redirects are followed""" with patch('httpx.AsyncClient') as mock_client: # The mock should be configured to test redirect behavior mock_response = MagicMock() mock_response.text = "Final destination" mock_client_instance = AsyncMock() mock_client_instance.get = AsyncMock(return_value=mock_response) mock_client.return_value.__aenter__.return_value = mock_client_instance result = await fetch_session.call_tool( "fetch_url", {"url": "https://short.url/redirect"} ) assert len(result.content) > 0 async def test_fetch_url_invalid_url(self, fetch_session): """Test handling of invalid URLs""" with patch('httpx.AsyncClient') as mock_client: mock_client_instance = AsyncMock() mock_client_instance.get = AsyncMock( side_effect=httpx.InvalidURL("Invalid URL") ) mock_client.return_value.__aenter__.return_value = mock_client_instance with pytest.raises(httpx.InvalidURL): await fetch_session.call_tool( "fetch_url", {"url": "not-a-valid-url"} ) async def test_fetch_url_timeout(self, fetch_session): """Test timeout handling""" with patch('httpx.AsyncClient') as mock_client: mock_client_instance = AsyncMock() mock_client_instance.get = AsyncMock( side_effect=httpx.TimeoutException("Timeout") ) mock_client.return_value.__aenter__.return_value = mock_client_instance with pytest.raises(httpx.TimeoutException): await fetch_session.call_tool( "fetch_url", {"url": "https://slow-site.com"} ) async def test_fetch_url_size_limit(self, fetch_session): """Test that very large responses are truncated""" with patch('httpx.AsyncClient') as mock_client: # Create very large HTML large_html = "" + ("X" * 10000) + "" mock_response = MagicMock() mock_response.text = large_html mock_client_instance = AsyncMock() mock_client_instance.get = AsyncMock(return_value=mock_response) mock_client.return_value.__aenter__.return_value = mock_client_instance result = await fetch_session.call_tool( "fetch_url", {"url": "https://example.com"} ) text = result.content[0].text # Should be truncated to 5000 chars (per implementation) assert len(text) <= 5000