Skip to main content
Glama

Fetch JSONPath MCP

by ackness
test_utils.py10.2 kB
import json from unittest.mock import AsyncMock, patch import pytest from jsonrpc_mcp.utils import batch_extract_json, batch_fetch_urls, fetch_url_content, get_http_client_config @pytest.mark.asyncio async def test_get_http_client_config(): """Test HTTP client configuration from environment""" with patch.dict('os.environ', { 'JSONRPC_MCP_TIMEOUT': '30.0', 'JSONRPC_MCP_VERIFY': 'false', 'JSONRPC_MCP_FOLLOW_REDIRECTS': 'true', 'JSONRPC_MCP_HEADERS': '{"Authorization": "Bearer token"}', 'JSONRPC_MCP_PROXY': 'http://proxy:8080' }): config = await get_http_client_config() assert config['timeout'] == 30.0 assert not config['verify'] assert config['follow_redirects'] # Check that custom headers are present and default headers exist assert "Authorization" in config['headers'] assert config['headers']["Authorization"] == "Bearer token" assert "User-Agent" in config['headers'] # Default browser header assert "Accept" in config['headers'] # Default browser header assert config['trust_env'] @pytest.mark.asyncio async def test_fetch_url_content_json(): """Test fetching JSON content with validation""" mock_response = AsyncMock() mock_response.text = '{"valid": "json"}' mock_response.raise_for_status = AsyncMock() with patch('httpx.AsyncClient') as mock_client: mock_client.return_value.__aenter__.return_value.get.return_value = mock_response result = await fetch_url_content("http://example.com", as_json=True) assert result == '{"valid": "json"}' @pytest.mark.asyncio async def test_fetch_url_content_invalid_json(): """Test fetching invalid JSON content raises error""" mock_response = AsyncMock() mock_response.text = 'not valid json' mock_response.raise_for_status = AsyncMock() with patch('httpx.AsyncClient') as mock_client: mock_client.return_value.__aenter__.return_value.get.return_value = mock_response with pytest.raises(Exception): await fetch_url_content("http://example.com", as_json=True) @pytest.mark.asyncio async def test_batch_fetch_urls_mixed_results(): """Test batch fetching with some failures""" async def mock_fetch(url, as_json=True, output_format="markdown"): if "fail" in url: raise Exception("Network error") return f"content from {url}" with patch('jsonrpc_mcp.utils.fetch_url_content', side_effect=mock_fetch): urls = ["http://success.com", "http://fail.com"] results = await batch_fetch_urls(urls, as_json=False) assert len(results) == 2 assert results[0]["success"] assert results[0]["content"] == "content from http://success.com" assert not results[1]["success"] assert "error" in results[1] @pytest.mark.asyncio async def test_batch_extract_json_mixed_results(): """Test batch JSON extraction with some failures""" async def mock_fetch(url, as_json=True, method="GET", data=None, headers=None, output_format="markdown"): if "fail" in url: raise Exception("Network error") if "invalid" in url: return "not json" return '{"data": [1, 2, 3]}' with patch('jsonrpc_mcp.utils.fetch_url_content', side_effect=mock_fetch): requests = [ {"url": "http://success.com", "pattern": "data[*]"}, {"url": "http://fail.com"}, {"url": "http://invalid.com"}, {"url": ""}, # Missing URL ] results = await batch_extract_json(requests) assert len(results) == 4 assert results[0]["success"] assert results[0]["content"] == [1, 2, 3] assert not results[1]["success"] assert not results[2]["success"] assert not results[3]["success"] assert results[3]["error"] == "Missing URL" @pytest.mark.asyncio async def test_batch_extract_json_no_pattern(): """Test batch JSON extraction without patterns""" with patch('jsonrpc_mcp.utils.fetch_url_content') as mock_fetch: mock_fetch.return_value = '{"full": "document"}' requests = [{"url": "http://example.com"}] results = await batch_extract_json(requests) assert len(results) == 1 assert results[0]["success"] assert results[0]["content"] == [{"full": "document"}] @pytest.mark.asyncio async def test_batch_extract_json_same_url_optimization(): """Test that same URL is only fetched once when multiple patterns are requested""" with patch('jsonrpc_mcp.utils.fetch_url_content') as mock_fetch: mock_fetch.return_value = '{"data": {"users": [{"name": "John"}, {"name": "Jane"}], "count": 2}}' # Multiple requests for the same URL with different patterns requests = [ {"url": "http://127.0.0.1:8080", "pattern": "data.users[*].name"}, {"url": "http://127.0.0.1:8080", "pattern": "data.count"}, {"url": "http://other.com", "pattern": "data.users"}, {"url": "http://127.0.0.1:8080", "pattern": "data.users"}, ] results = await batch_extract_json(requests) # Verify fetch_url_content was called only twice (once for each unique URL) assert mock_fetch.call_count == 2 # Verify all results are present and in the same order as requests assert len(results) == 4 # First request: extract names assert results[0]["success"] assert results[0]["url"] == "http://127.0.0.1:8080" assert results[0]["pattern"] == "data.users[*].name" assert results[0]["content"] == ["John", "Jane"] # Second request: extract count assert results[1]["success"] assert results[1]["url"] == "http://127.0.0.1:8080" assert results[1]["pattern"] == "data.count" assert results[1]["content"] == [2] # Third request: different URL assert results[2]["success"] assert results[2]["url"] == "http://other.com" assert results[2]["pattern"] == "data.users" # Fourth request: same URL as first two assert results[3]["success"] assert results[3]["url"] == "http://127.0.0.1:8080" assert results[3]["pattern"] == "data.users" assert results[3]["content"] == [[{"name": "John"}, {"name": "Jane"}]] @pytest.mark.asyncio async def test_extract_json_extensions(): """Test JSONPath extensions like len, keys, arithmetic, filtering""" from jsonrpc_mcp.utils import extract_json test_data = json.dumps({ "users": [ {"name": "John", "age": 30, "city": "NYC"}, {"name": "Jane", "age": 25, "city": "LA"}, {"name": "Bob", "age": 35, "city": "NYC"} ], "metadata": { "total": 3, "source": "api_v1" }, "tags": "user,profile,test" }) # Test len extension result = extract_json(test_data, "$.users.`len`") assert result == [3] # Test keys extension result = extract_json(test_data, "$.metadata.`keys`") assert set(result) == {"total", "source"} # Test filtering result = extract_json(test_data, "$.users[?(@.age > 28)].name") assert set(result) == {"John", "Bob"} # Test arithmetic operations result = extract_json(test_data, "$.metadata.total + 1") assert result == [4] # Test string operations (split requires 3 params: separator, segment_index, max_splits) result = extract_json(test_data, "$.tags.`split(\",\", *, -1)`") assert result == [["user", "profile", "test"]] # Test multiple conditions filtering result = extract_json(test_data, "$.users[?(@.age > 25 & @.city = \"NYC\")].name") assert set(result) == {"John", "Bob"} # Both John (30) and Bob (35) are > 25 and in NYC @pytest.mark.asyncio async def test_extract_json_complex_extensions(): """Test more complex JSONPath extension scenarios""" from jsonrpc_mcp.utils import extract_json test_data = json.dumps({ "products": [ {"name": "Apple", "price": 1.5, "category": "fruit", "stock": 100}, {"name": "Banana", "price": 0.8, "category": "fruit", "stock": 150}, {"name": "Carrot", "price": 2.0, "category": "vegetable", "stock": 80}, {"name": "Broccoli", "price": 3.5, "category": "vegetable", "stock": 60} ], "store_info": { "name": "Fresh Market", "location": "Downtown_Plaza" } }) # Test arithmetic with arrays result = extract_json(test_data, "$.products[*].price + $.products[*].stock") expected = [101.5, 150.8, 82.0, 63.5] # price + stock for each item assert result == expected # Test string replacement - skip for now due to syntax complexity # result = extract_json(test_data, "$.store_info.location.`sub(/_/, \" \")`") # assert result == ["Downtown Plaza"] # Test filtering with complex conditions result = extract_json(test_data, "$.products[?(@.category = \"fruit\" & @.price < 1.0)].name") assert result == ["Banana"] # Test str() conversion result = extract_json(test_data, "$.products[0].price.`str()`") assert result == ["1.5"] # Test length of array result = extract_json(test_data, "$.products.`len`") assert result == [4] @pytest.mark.asyncio async def test_extract_json_fallback_to_basic_parser(): """Test that basic patterns still work and fallback works correctly""" from jsonrpc_mcp.utils import extract_json test_data = json.dumps({ "simple": {"nested": {"value": 42}}, "array": [1, 2, 3, 4, 5] }) # Basic patterns should work with both parsers result = extract_json(test_data, "$.simple.nested.value") assert result == [42] result = extract_json(test_data, "$.array[2:4]") assert result == [3, 4] result = extract_json(test_data, "$.array[*]") assert result == [1, 2, 3, 4, 5]

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/ackness/fetch-jsonpath-mcp'

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