Skip to main content
Glama
test_wiki_page_parse.py22.7 kB
"""Test suite for MediaWiki page parse handlers.""" from unittest.mock import MagicMock import pytest from mediawiki_api_mcp.client import MediaWikiClient from mediawiki_api_mcp.handlers.wiki_page_parse import handle_parse_page class TestParseHandlers: """Test cases for parse-related handlers.""" @pytest.fixture def mock_client(self): """Create a mock MediaWiki client.""" client = MagicMock(spec=MediaWikiClient) return client @pytest.mark.asyncio async def test_handle_parse_page_success(self, mock_client): """Test successful page parsing.""" # Setup mock response mock_client.parse_page.return_value = { "parse": { "title": "Test Page", "pageid": 12345, "revid": 67890, "text": {"*": "<p>Hello world</p>"}, "wikitext": {"*": "Hello world"}, "categories": [{"*": "Category:Test"}], "links": [{"*": "Another Page"}], "sections": [{"level": 1, "line": "Introduction"}] } } arguments = { "title": "Test Page" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 assert "Parse Results for: Test Page" in result[0].text assert "Page ID: 12345" in result[0].text assert "Revision ID: 67890" in result[0].text assert "## Parsed HTML" in result[0].text assert "Hello world" in result[0].text @pytest.mark.asyncio async def test_handle_parse_page_by_text(self, mock_client): """Test parsing arbitrary text.""" mock_client.parse_page.return_value = { "parse": { "title": "API", "pageid": 0, "revid": 0, "text": {"*": "<p>Some <b>bold</b> text</p>"}, "wikitext": {"*": "Some '''bold''' text"} } } arguments = { "text": "Some '''bold''' text", "title": "Test" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 assert "Parse Results for: API" in result[0].text assert "## Parsed HTML" in result[0].text assert "bold" in result[0].text @pytest.mark.asyncio async def test_handle_parse_page_with_prop_filter(self, mock_client): """Test parsing with specific prop filter.""" mock_client.parse_page.return_value = { "parse": { "title": "Test Page", "pageid": 12345, "revid": 67890, "text": {"*": "<p>Hello world</p>"}, "categories": [{"*": "Category:Test"}] } } arguments = { "title": "Test Page", "prop": "text|categories" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 assert "Requested properties: text, categories" in result[0].text assert "## Parsed HTML" in result[0].text assert "## Categories" in result[0].text @pytest.mark.asyncio async def test_handle_parse_page_missing_content_source(self, mock_client): """Test parse page with missing content source parameters.""" arguments = {} result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 assert "Error: Must provide one of: title, pageid, oldid, text, page, or summary" in result[0].text @pytest.mark.asyncio async def test_handle_parse_page_unexpected_response(self, mock_client): """Test parse page with unexpected API response.""" mock_client.parse_page.return_value = { "error": "Invalid request" } arguments = { "title": "Test Page" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 assert "MediaWiki API Error: Invalid request" in result[0].text @pytest.mark.asyncio async def test_handle_parse_page_exception(self, mock_client): """Test parse page with client exception.""" mock_client.parse_page.side_effect = Exception("Network error") arguments = { "title": "Test Page" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 assert "Error parsing content: Network error" in result[0].text @pytest.mark.asyncio async def test_handle_parse_page_comprehensive_output(self, mock_client): """Test parse page with comprehensive output sections.""" mock_client.parse_page.return_value = { "parse": { "title": "Comprehensive Page", "pageid": 12345, "revid": 67890, "text": {"*": "<p>Main content</p>"}, "wikitext": {"*": "Main content"}, "categories": [{"*": "Category:Test"}, {"*": "Category:Example"}], "links": [{"*": "Link One"}, {"*": "Link Two"}], "templates": [{"*": "Template:Info"}], "images": ["File:Example.jpg"], "externallinks": ["https://example.com"], "sections": [ {"level": 1, "line": "Introduction"}, {"level": 2, "line": "Details"} ], "langlinks": [{"lang": "es", "*": "Página de prueba"}], "iwlinks": [{"prefix": "w", "*": "Main Page"}], "properties": [{"name": "displaytitle", "*": "Display Title"}], "parsewarnings": ["Warning message"], "displaytitle": "Custom Display Title" } } arguments = { "title": "Comprehensive Page" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 content = result[0].text # Check that all sections are present assert "## Parsed HTML" in content assert "## Wikitext" in content assert "## Categories" in content assert "## Internal Links" in content assert "## Templates" in content assert "## Images" in content assert "## External Links" in content assert "## Sections" in content assert "## Language Links" in content assert "## Interwiki Links" in content assert "## Properties" in content assert "## Parse Warnings" in content assert "## Display Title" in content # Check specific content assert "Category:Test" in content assert "Link One" in content assert "Template:Info" in content assert "File:Example.jpg" in content assert "https://example.com" in content assert "Level 1: Introduction" in content assert "es: Página de prueba" in content assert "w: Main Page" in content assert "Custom Display Title" in content @pytest.mark.asyncio async def test_handle_parse_page_prop_parsing(self, mock_client): """Test proper parsing of prop parameter from string to list.""" mock_client.parse_page.return_value = { "parse": { "title": "Test Page", "pageid": 12345, "revid": 67890, "text": {"*": "<p>Test content</p>"} } } arguments = { "title": "Test Page", "prop": "text | categories | links" # Test with spaces } await handle_parse_page(mock_client, arguments) # Verify the prop was properly parsed and passed to the client mock_client.parse_page.assert_called_once() call_args = mock_client.parse_page.call_args assert call_args.kwargs["prop"] == ["text", "categories", "links"] @pytest.mark.asyncio async def test_handle_parse_page_template_sandbox_parsing(self, mock_client): """Test proper parsing of template sandbox prefix parameter.""" mock_client.parse_page.return_value = { "parse": { "title": "Test Page", "pageid": 12345, "revid": 67890, "text": {"*": "<p>Test content</p>"} } } arguments = { "title": "Test Page", "templatesandboxprefix": "User:Test | User:Demo" # Test with spaces } await handle_parse_page(mock_client, arguments) # Verify the templatesandboxprefix was properly parsed and passed to the client mock_client.parse_page.assert_called_once() call_args = mock_client.parse_page.call_args assert call_args.kwargs["templatesandboxprefix"] == ["User:Test", "User:Demo"] @pytest.mark.asyncio async def test_handle_parse_page_invalid_section(self, mock_client): """Test parse page with invalid section parameter.""" # Mock a validation error mock_client.parse_page.side_effect = ValueError("Section parameter must be a number, 'new', or 'T-' prefixed template section") arguments = { "title": "Test Page", "section": "invalid_section" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 assert "Section parameter must be a number, 'new', or 'T-' prefixed template section" in result[0].text @pytest.mark.asyncio async def test_handle_parse_page_valid_section_types(self, mock_client): """Test parse page with valid section parameter types.""" mock_client.parse_page.return_value = { "parse": { "title": "Test Page", "pageid": 12345, "revid": 67890, "text": {"*": "<p>Section content</p>"} } } # Test integer section arguments = { "title": "Test Page", "section": "1" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 assert "Parse Results for: Test Page" in result[0].text # Test "new" section arguments["section"] = "new" result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 assert "Parse Results for: Test Page" in result[0].text # Test template section arguments["section"] = "T-1" result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 assert "Parse Results for: Test Page" in result[0].text @pytest.mark.asyncio async def test_handle_parse_page_minimal_content_detection(self, mock_client): """Test detection of minimal content issues.""" # Test minimal content that should trigger warning mock_client.parse_page.return_value = { "parse": { "title": "Test Page", "pageid": 12345, "revid": 67890, "text": {"*": '<div class="mw-content-ltr mw-parser-output" lang="en" dir="ltr"></div>'} } } arguments = { "title": "Test Page" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 assert "WARNING: Content appears minimal" in result[0].text @pytest.mark.asyncio async def test_handle_parse_page_missing_content(self, mock_client): """Test detection of completely missing content.""" # Test no text content returned for existing page mock_client.parse_page.return_value = { "parse": { "title": "Test Page", "pageid": 12345, "revid": 67890 # No "text" field } } arguments = { "title": "Test Page" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 assert "WARNING: No text content in parse result" in result[0].text @pytest.mark.asyncio async def test_handle_parse_page_summary_only(self, mock_client): """Test parsing summary parameter independently.""" mock_client.parse_page.return_value = { "parse": { "title": "API", "pageid": 0, "revid": 0, "text": {"*": "<p>Some <b>summary</b> content</p>"} } } arguments = { "summary": "Some [[link]] summary" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 assert "Parse Results for: API" in result[0].text # Verify the summary parameter was passed correctly mock_client.parse_page.assert_called_once() call_args = mock_client.parse_page.call_args assert call_args.kwargs["summary"] == "Some [[link]] summary" # Verify prop was set to empty list for summary parsing assert call_args.kwargs["prop"] == [] @pytest.mark.asyncio async def test_handle_parse_page_summary_fallback_mechanism(self, mock_client): """Test fallback mechanism when summary parsing returns minimal content.""" # First call (summary parsing) returns minimal content # Second call (fallback text parsing) returns proper content side_effect_values = [ { "parse": { "title": "API", "pageid": 0, "revid": 0, "text": {"*": '<div class="mw-parser-output"></div>'} # Minimal content } }, { "parse": { "title": "API", "pageid": 0, "revid": 0, "text": {"*": "<p>Some <a href=\"/wiki/Link\">link</a> and <b>formatting</b></p>"}, "links": [{"*": "Link"}], "parsewarnings": [] } } ] mock_client.parse_page.side_effect = side_effect_values arguments = { "summary": "Some [[link]] and '''formatting'''" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 result_text = result[0].text # Should show the fallback worked assert "Parse Results for: API" in result_text assert "Some <a href=\"/wiki/Link\">link</a>" in result_text or "Link" in result_text assert "formatting" in result_text # Verify both calls were made - first for summary, then for fallback assert mock_client.parse_page.call_count == 2 # Check first call (summary parsing) first_call = mock_client.parse_page.call_args_list[0] assert first_call.kwargs["summary"] == "Some [[link]] and '''formatting'''" assert first_call.kwargs["prop"] == [] # Check second call (fallback text parsing) second_call = mock_client.parse_page.call_args_list[1] assert second_call.kwargs["text"] == "Some [[link]] and '''formatting'''" assert second_call.kwargs["contentmodel"] == "wikitext" assert "text" in second_call.kwargs["prop"] @pytest.mark.asyncio async def test_handle_parse_page_summary_no_fallback_when_content_good(self, mock_client): """Test that fallback is not triggered when summary parsing returns good content.""" mock_client.parse_page.return_value = { "parse": { "title": "API", "pageid": 0, "revid": 0, "text": {"*": "<p>Good summary content with <a href=\"/wiki/Link\">link</a> and <b>formatting</b></p>"} } } arguments = { "summary": "Good summary with [[link]] and '''formatting'''" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 result_text = result[0].text # Should show the good content assert "Good summary content" in result_text assert "link" in result_text assert "formatting" in result_text # Should only make one call (no fallback needed) assert mock_client.parse_page.call_count == 1 # Verify it was the summary call call_args = mock_client.parse_page.call_args assert call_args.kwargs["summary"] == "Good summary with [[link]] and '''formatting'''" @pytest.mark.asyncio async def test_minimal_content_detection(self): """Test the _is_minimal_content helper function.""" from mediawiki_api_mcp.handlers.wiki_page_parse import _is_minimal_content # Should detect minimal content assert _is_minimal_content("") assert _is_minimal_content(" ") assert _is_minimal_content("<p></p>") assert _is_minimal_content("<div></div>") assert _is_minimal_content('<div class="mw-parser-output"></div>') assert _is_minimal_content('<div class="mw-parser-output"> </div>') # Should not detect good content as minimal assert not _is_minimal_content("<p>Real content here</p>") assert not _is_minimal_content("<p>Some <b>formatted</b> text</p>") assert not _is_minimal_content('<div class="mw-parser-output"><p>Actual content</p></div>') @pytest.mark.asyncio async def test_title_vs_page_parameter_consistency(self, mock_client): """Test that title and page parameters produce identical API calls.""" mock_client.parse_page.return_value = { "parse": { "title": "Test Page", "pageid": 12345, "revid": 67890, "text": {"*": "<p>Test content</p>"} } } # Test with title parameter arguments_title = {"title": "Test Page"} await handle_parse_page(mock_client, arguments_title) # Get the call arguments for title title_call_args = mock_client.parse_page.call_args # Reset mock mock_client.reset_mock() mock_client.parse_page.return_value = { "parse": { "title": "Test Page", "pageid": 12345, "revid": 67890, "text": {"*": "<p>Test content</p>"} } } # Test with page parameter arguments_page = {"page": "Test Page"} await handle_parse_page(mock_client, arguments_page) # Get the call arguments for page page_call_args = mock_client.parse_page.call_args # The API calls should be identical - both should use page parameter assert title_call_args.kwargs.get("page") == "Test Page" assert page_call_args.kwargs.get("page") == "Test Page" assert title_call_args.kwargs.get("title") is None # title should not be passed to API assert page_call_args.kwargs.get("title") is None @pytest.mark.asyncio async def test_enhanced_error_handling(self, mock_client): """Test enhanced error handling with detailed diagnostic information.""" # Test case where page is missing mock_client.parse_page.return_value = { "query": { "pages": { "-1": { "missing": True, "title": "Nonexistent Page" } } } } arguments = { "title": "Nonexistent Page" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 error_text = result[0].text # Check that enhanced error information is present assert "Error: Unexpected response format from Parse API" in error_text assert "Response keys: ['query']" in error_text assert "Received 'query' response - this indicates the wrong API endpoint was used" in error_text assert "Used parameters: title='Nonexistent Page'" in error_text assert "page(s) marked as missing in query response" in error_text @pytest.mark.asyncio async def test_enhanced_error_handling_invalid_params(self, mock_client): """Test enhanced error handling for invalid parameter combinations.""" # Test case with parameter conflict error mock_client.parse_page.return_value = { "error": { "code": "invalidparammix", "info": "The parameters page, title cannot be used together." } } arguments = { "title": "Test Page", "text": "Some text" } result = await handle_parse_page(mock_client, arguments) assert len(result) == 1 error_text = result[0].text # Should handle this as a regular API error, not unexpected response assert "MediaWiki API Error (invalidparammix)" in error_text assert "parameters page, title cannot be used together" in error_text @pytest.mark.asyncio async def test_empty_prop_parameter_included_in_api_request(self, mock_client): """Test that empty prop parameter is correctly included in API request for summary parsing.""" from unittest.mock import AsyncMock, create_autospec # Create a properly mocked auth client from mediawiki_api_mcp.client_modules.client_auth import MediaWikiAuthClient mock_auth = create_autospec(MediaWikiAuthClient, spec_set=True, instance=True) mock_auth._make_request = AsyncMock(return_value={ "parse": { "title": "API", "pageid": 0, "revid": 0, "text": {"*": "<p>Summary content</p>"} } }) # Create the actual client instance to test from mediawiki_api_mcp.client_modules.client_page import MediaWikiPageClient page_client = MediaWikiPageClient(mock_auth) # Test summary parsing with empty prop await page_client.parse_page(summary="Test [[link]] summary") # Verify the API request was made with prop="" mock_auth._make_request.assert_called_once() call_args = mock_auth._make_request.call_args api_params = call_args[1]['params'] # GET request uses params # The critical fix: prop should be present with empty value assert 'prop' in api_params assert api_params['prop'] == "" # Empty string, not missing assert api_params['summary'] == "Test [[link]] summary" assert api_params['action'] == "parse"

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/entanglr/mediawiki-api-mcp'

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