Skip to main content
Glama
kaman05010

MCP Wikipedia Server

by kaman05010
test_server.py22.6 kB
""" Test suite for MCP Wikipedia Server. This module contains comprehensive tests for all Wikipedia tools and MCP protocol compliance. """ import pytest import asyncio import json import time from unittest.mock import Mock, patch, AsyncMock from typing import Dict, Any, List # Import the server module import sys import os sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) try: from mcp_server.mcp_server import WikipediaServer except ImportError: # Fallback for different import paths sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src', 'mcp_server')) from mcp_server import WikipediaServer class TestWikipediaServer: """Test cases for the WikipediaServer class.""" @pytest.fixture def server(self): """Create a server instance for testing.""" return WikipediaServer() @pytest.fixture def mock_wikipedia_success(self): """Mock successful Wikipedia API responses.""" with patch('wikipedia.search') as mock_search, \ patch('wikipedia.page') as mock_page, \ patch('wikipedia.summary') as mock_summary: # Mock search results mock_search.return_value = ["Test Article", "Another Article"] # Mock page object mock_page_obj = Mock() mock_page_obj.title = "Test Article" mock_page_obj.url = "https://en.wikipedia.org/wiki/Test_Article" mock_page_obj.content = "This is test content for the article." mock_page_obj.sections = ["Introduction", "History", "Overview", "References"] mock_page_obj.section = Mock(return_value="This is the history section content.") mock_page_obj.pageid = 12345 mock_page_obj.categories = ["Test Category", "Example Category"] mock_page_obj.links = ["Related Article 1", "Related Article 2"] mock_page.return_value = mock_page_obj # Mock summary mock_summary.return_value = "This is a test summary of the article." yield { 'search': mock_search, 'page': mock_page, 'summary': mock_summary, 'page_obj': mock_page_obj } class TestFetchWikipediaInfo: """Test cases for fetch_wikipedia_info tool.""" @pytest.fixture def server(self): return WikipediaServer() @pytest.mark.asyncio async def test_fetch_info_success(self, server): """Test successful Wikipedia article retrieval.""" with patch('wikipedia.search') as mock_search, \ patch('wikipedia.page') as mock_page: # Setup mocks mock_search.return_value = ["Python (programming language)"] mock_page_obj = Mock() mock_page_obj.title = "Python (programming language)" mock_page_obj.url = "https://en.wikipedia.org/wiki/Python_(programming_language)" mock_page_obj.summary = "Python is a high-level programming language." mock_page_obj.pageid = 23862 mock_page_obj.categories = ["Programming languages", "Python"] mock_page_obj.links = ["Programming", "Guido van Rossum"] mock_page.return_value = mock_page_obj # Test the function result = await server.fetch_wikipedia_info("Python programming") # Assertions assert result["success"] is True assert "data" in result assert result["data"]["title"] == "Python (programming language)" assert result["data"]["url"] == "https://en.wikipedia.org/wiki/Python_(programming_language)" assert result["data"]["page_id"] == 23862 assert "summary" in result["data"] assert "categories" in result["data"] assert "links" in result["data"] assert "metadata" in result assert result["metadata"]["query"] == "Python programming" @pytest.mark.asyncio async def test_fetch_info_not_found(self, server): """Test handling of non-existent articles.""" with patch('wikipedia.search') as mock_search: # Mock empty search results mock_search.return_value = [] result = await server.fetch_wikipedia_info("NonExistentArticle12345") assert result["success"] is False assert "error" in result assert "not found" in result["error"].lower() assert result["metadata"]["query"] == "NonExistentArticle12345" @pytest.mark.asyncio async def test_fetch_info_disambiguation(self, server): """Test handling of disambiguation pages.""" import wikipedia with patch('wikipedia.search') as mock_search, \ patch('wikipedia.page') as mock_page: mock_search.return_value = ["Python", "Python (programming language)"] # Mock disambiguation exception mock_page.side_effect = wikipedia.DisambiguationError( "Python", ["Python (programming language)", "Python (snake)", "Python (mythology)"] ) result = await server.fetch_wikipedia_info("Python") assert result["success"] is False assert "disambiguation" in result["error"].lower() or "suggestions" in result @pytest.mark.asyncio async def test_fetch_info_network_error(self, server): """Test handling of network errors.""" with patch('wikipedia.search') as mock_search: # Mock network error mock_search.side_effect = Exception("Network connection failed") result = await server.fetch_wikipedia_info("Test Query") assert result["success"] is False assert "error" in result assert "metadata" in result @pytest.mark.asyncio async def test_fetch_info_empty_query(self, server): """Test handling of empty queries.""" result = await server.fetch_wikipedia_info("") assert result["success"] is False assert "error" in result assert "empty" in result["error"].lower() or "invalid" in result["error"].lower() @pytest.mark.asyncio async def test_fetch_info_very_long_query(self, server): """Test handling of extremely long queries.""" long_query = "x" * 5000 # Very long query result = await server.fetch_wikipedia_info(long_query) # Should handle gracefully (either succeed or fail gracefully) assert "success" in result assert "metadata" in result class TestListWikipediaSections: """Test cases for list_wikipedia_sections tool.""" @pytest.fixture def server(self): return WikipediaServer() @pytest.mark.asyncio async def test_list_sections_success(self, server): """Test successful section listing.""" with patch('wikipedia.page') as mock_page: # Setup mock page with sections mock_page_obj = Mock() mock_page_obj.title = "Machine Learning" mock_page_obj.sections = [ "Overview", "History", "Types", "Supervised learning", "Unsupervised learning", "Applications", "Ethics", "References" ] mock_page_obj.pageid = 233488 mock_page.return_value = mock_page_obj result = await server.list_wikipedia_sections("Machine Learning") assert result["success"] is True assert "data" in result assert result["data"]["article_title"] == "Machine Learning" assert "sections" in result["data"] assert len(result["data"]["sections"]) > 0 assert result["data"]["total_sections"] == len(result["data"]["sections"]) # Check section structure first_section = result["data"]["sections"][0] assert "title" in first_section assert "level" in first_section assert "index" in first_section assert "metadata" in result assert result["metadata"]["topic"] == "Machine Learning" @pytest.mark.asyncio async def test_list_sections_not_found(self, server): """Test handling when article is not found.""" import wikipedia with patch('wikipedia.page') as mock_page: # Mock page not found mock_page.side_effect = wikipedia.PageError("Article not found") result = await server.list_wikipedia_sections("NonExistentArticle") assert result["success"] is False assert "error" in result assert "not found" in result["error"].lower() @pytest.mark.asyncio async def test_list_sections_no_sections(self, server): """Test handling of articles with no sections.""" with patch('wikipedia.page') as mock_page: # Setup mock page with no sections mock_page_obj = Mock() mock_page_obj.title = "Simple Article" mock_page_obj.sections = [] mock_page_obj.pageid = 12345 mock_page.return_value = mock_page_obj result = await server.list_wikipedia_sections("Simple Article") assert result["success"] is True assert result["data"]["total_sections"] == 0 assert len(result["data"]["sections"]) == 0 class TestGetSectionContent: """Test cases for get_section_content tool.""" @pytest.fixture def server(self): return WikipediaServer() @pytest.mark.asyncio async def test_get_section_content_success(self, server): """Test successful section content retrieval.""" with patch('wikipedia.page') as mock_page: # Setup mock page mock_page_obj = Mock() mock_page_obj.title = "Python (programming language)" mock_page_obj.section.return_value = "Python was conceived in the late 1980s by Guido van Rossum..." mock_page_obj.sections = ["Overview", "History", "Features", "References"] mock_page_obj.pageid = 23862 mock_page.return_value = mock_page_obj result = await server.get_section_content("Python programming", "History") assert result["success"] is True assert "data" in result assert result["data"]["article_title"] == "Python (programming language)" assert result["data"]["section_title"] == "History" assert "content" in result["data"] assert len(result["data"]["content"]) > 0 assert "Guido van Rossum" in result["data"]["content"] assert "content_length" in result["data"] assert result["data"]["content_length"] > 0 assert "metadata" in result assert result["metadata"]["topic"] == "Python programming" assert result["metadata"]["section_title"] == "History" @pytest.mark.asyncio async def test_get_section_content_section_not_found(self, server): """Test handling when section is not found.""" with patch('wikipedia.page') as mock_page: # Setup mock page mock_page_obj = Mock() mock_page_obj.title = "Test Article" mock_page_obj.section.return_value = None # Section not found mock_page_obj.sections = ["Overview", "History", "References"] mock_page_obj.pageid = 12345 mock_page.return_value = mock_page_obj result = await server.get_section_content("Test Article", "NonExistentSection") assert result["success"] is False assert "error" in result assert "section" in result["error"].lower() assert "not found" in result["error"].lower() @pytest.mark.asyncio async def test_get_section_content_article_not_found(self, server): """Test handling when article is not found.""" import wikipedia with patch('wikipedia.page') as mock_page: # Mock article not found mock_page.side_effect = wikipedia.PageError("Article not found") result = await server.get_section_content("NonExistentArticle", "History") assert result["success"] is False assert "error" in result assert "not found" in result["error"].lower() @pytest.mark.asyncio async def test_get_section_content_empty_section(self, server): """Test handling of empty section content.""" with patch('wikipedia.page') as mock_page: # Setup mock page with empty section mock_page_obj = Mock() mock_page_obj.title = "Test Article" mock_page_obj.section.return_value = "" # Empty section mock_page_obj.sections = ["Overview", "EmptySection", "References"] mock_page_obj.pageid = 12345 mock_page.return_value = mock_page_obj result = await server.get_section_content("Test Article", "EmptySection") assert result["success"] is True assert result["data"]["content"] == "" assert result["data"]["content_length"] == 0 class TestPerformance: """Performance and load testing.""" @pytest.fixture def server(self): return WikipediaServer() @pytest.mark.asyncio async def test_response_time_search(self, server): """Test that search response times are reasonable.""" with patch('wikipedia.search') as mock_search, \ patch('wikipedia.page') as mock_page: # Setup fast mocks mock_search.return_value = ["Test Article"] mock_page_obj = Mock() mock_page_obj.title = "Test Article" mock_page_obj.summary = "Test summary" mock_page_obj.url = "https://test.url" mock_page_obj.pageid = 12345 mock_page_obj.categories = [] mock_page_obj.links = [] mock_page.return_value = mock_page_obj # Measure response time start_time = time.time() result = await server.fetch_wikipedia_info("Test Query") end_time = time.time() response_time = end_time - start_time assert result["success"] is True assert response_time < 1.0 # Should complete within 1 second with mocks @pytest.mark.asyncio async def test_concurrent_requests(self, server): """Test handling of concurrent requests.""" with patch('wikipedia.search') as mock_search, \ patch('wikipedia.page') as mock_page: # Setup mocks mock_search.return_value = ["Test Article"] mock_page_obj = Mock() mock_page_obj.title = "Test Article" mock_page_obj.summary = "Test summary" mock_page_obj.url = "https://test.url" mock_page_obj.pageid = 12345 mock_page_obj.categories = [] mock_page_obj.links = [] mock_page.return_value = mock_page_obj # Create concurrent requests tasks = [ server.fetch_wikipedia_info(f"Query {i}") for i in range(5) ] start_time = time.time() results = await asyncio.gather(*tasks) end_time = time.time() total_time = end_time - start_time # All requests should succeed assert all(result["success"] for result in results) # Should be faster than sequential execution assert total_time < 5.0 # Much faster than 5 sequential requests class TestErrorHandling: """Test error handling and edge cases.""" @pytest.fixture def server(self): return WikipediaServer() @pytest.mark.asyncio async def test_wikipedia_timeout(self, server): """Test handling of Wikipedia API timeouts.""" import wikipedia with patch('wikipedia.search') as mock_search: # Mock timeout exception mock_search.side_effect = wikipedia.exceptions.HTTPTimeoutError("Timeout") result = await server.fetch_wikipedia_info("Test Query") assert result["success"] is False assert "error" in result assert "timeout" in result["error"].lower() @pytest.mark.asyncio async def test_rate_limit_handling(self, server): """Test handling of rate limiting.""" with patch('wikipedia.search') as mock_search: # Mock rate limit exception mock_search.side_effect = Exception("Rate limited") result = await server.fetch_wikipedia_info("Test Query") assert result["success"] is False assert "error" in result @pytest.mark.asyncio async def test_malformed_input(self, server): """Test handling of malformed inputs.""" # Test with various problematic inputs problematic_inputs = [ None, 123, # Non-string {"query": "test"}, # Dictionary instead of string ["test", "query"], # List instead of string ] for bad_input in problematic_inputs: try: result = await server.fetch_wikipedia_info(bad_input) # Should either handle gracefully or raise TypeError if isinstance(result, dict): assert "success" in result if not result["success"]: assert "error" in result except (TypeError, AttributeError): # Acceptable to raise type errors for invalid inputs pass class TestUtilities: """Test utility functions and helpers.""" @pytest.fixture def server(self): return WikipediaServer() def test_server_initialization(self, server): """Test that server initializes correctly.""" assert server is not None assert hasattr(server, 'fetch_wikipedia_info') assert hasattr(server, 'list_wikipedia_sections') assert hasattr(server, 'get_section_content') @pytest.mark.asyncio async def test_metadata_consistency(self, server): """Test that metadata is consistently included in responses.""" with patch('wikipedia.search') as mock_search, \ patch('wikipedia.page') as mock_page: # Setup mocks mock_search.return_value = ["Test Article"] mock_page_obj = Mock() mock_page_obj.title = "Test Article" mock_page_obj.summary = "Test summary" mock_page_obj.url = "https://test.url" mock_page_obj.pageid = 12345 mock_page_obj.categories = [] mock_page_obj.links = [] mock_page_obj.sections = ["Overview", "History"] mock_page_obj.section.return_value = "Section content" mock_page.return_value = mock_page_obj # Test all tools include metadata search_result = await server.fetch_wikipedia_info("Test") sections_result = await server.list_wikipedia_sections("Test") content_result = await server.get_section_content("Test", "History") for result in [search_result, sections_result, content_result]: assert "metadata" in result assert "timestamp" in result["metadata"] # Successful results should have consistent structure if result["success"]: assert "data" in result else: assert "error" in result # Integration test that can be run independently @pytest.mark.asyncio async def test_full_wikipedia_workflow(): """Integration test for complete Wikipedia research workflow.""" server = WikipediaServer() with patch('wikipedia.search') as mock_search, \ patch('wikipedia.page') as mock_page: # Setup comprehensive mocks mock_search.return_value = ["Python (programming language)"] mock_page_obj = Mock() mock_page_obj.title = "Python (programming language)" mock_page_obj.summary = "Python is a programming language." mock_page_obj.url = "https://en.wikipedia.org/wiki/Python_(programming_language)" mock_page_obj.pageid = 23862 mock_page_obj.categories = ["Programming languages"] mock_page_obj.links = ["Programming", "Software"] mock_page_obj.sections = ["Overview", "History", "Features", "Applications"] mock_page_obj.section.return_value = "Python was created by Guido van Rossum." mock_page.return_value = mock_page_obj # Step 1: Search for article search_result = await server.fetch_wikipedia_info("Python programming") assert search_result["success"] is True article_title = search_result["data"]["title"] # Step 2: Get sections sections_result = await server.list_wikipedia_sections(article_title) assert sections_result["success"] is True sections = sections_result["data"]["sections"] assert len(sections) > 0 # Step 3: Get content from first section first_section = sections[0]["title"] content_result = await server.get_section_content(article_title, first_section) assert content_result["success"] is True assert len(content_result["data"]["content"]) > 0 print("✅ Full workflow test completed successfully") if __name__ == "__main__": # Allow running tests directly pytest.main([__file__, "-v"])

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kaman05010/MCPClientServer'

If you have feedback or need assistance with the MCP directory API, please join our Discord server