Skip to content

Commit 3223051

Browse files
committed
Fix unit tests related to _meta JSON error parsing, mock misconfiguration, and version negotiation array index checks
1 parent 7596802 commit 3223051

9 files changed

Lines changed: 217 additions & 210 deletions

File tree

packages/toolbox-core/src/toolbox_core/mcp_transport/v20241105/mcp.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,16 +117,23 @@ async def _send_request(
117117
)
118118
except ValueError:
119119
raise RuntimeError(
120-
f"Invalid state: current protocol {self._protocol_version} is not in supported_protocols."
120+
f"Invalid state: current protocol "\
121+
f"{self._protocol_version} is not in "
122+
f"supported_protocols."
121123
)
122124

123125
try:
124126
err = types.JSONRPCError.model_validate(json_resp).error
127+
except Exception:
128+
err = None
129+
130+
if err:
125131
raise RuntimeError(
126132
f"MCP request failed with code {err.code}: {err.message}"
127133
)
128-
except Exception:
129-
raise RuntimeError(f"MCP request failed: {json_resp.get('error')}")
134+
else:
135+
raw_error = json_resp.get("error", {})
136+
raise RuntimeError(f"MCP request failed: {raw_error}")
130137

131138
if not response.ok:
132139
raise RuntimeError(

packages/toolbox-core/src/toolbox_core/mcp_transport/v20250326/mcp.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,16 +131,21 @@ async def _send_request(
131131
)
132132
except ValueError:
133133
raise RuntimeError(
134-
f"Invalid state: current protocol {self._protocol_version} is not in supported_protocols."
134+
f"Invalid state: current protocol "\
135+
f"{self._protocol_version} is not in "
136+
f"supported_protocols."
135137
)
136138

137139
try:
138140
err = types.JSONRPCError.model_validate(json_resp).error
141+
except Exception:
142+
err = None
143+
144+
if err:
139145
raise RuntimeError(
140146
f"MCP request failed with code {err.code}: {err.message}"
141147
)
142-
except Exception:
143-
# Fallback if the error doesn't match our schema exactly
148+
else:
144149
raw_error = json_resp.get("error", {})
145150
raise RuntimeError(f"MCP request failed: {raw_error}")
146151

packages/toolbox-core/src/toolbox_core/mcp_transport/v20250618/mcp.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,21 @@ async def _send_request(
122122
)
123123
except ValueError:
124124
raise RuntimeError(
125-
f"Invalid state: current protocol {self._protocol_version} is not in supported_protocols."
125+
f"Invalid state: current protocol "\
126+
f"{self._protocol_version} is not in "
127+
f"supported_protocols."
126128
)
127129

128130
try:
129131
err = types.JSONRPCError.model_validate(json_resp).error
132+
except Exception:
133+
err = None
134+
135+
if err:
130136
raise RuntimeError(
131137
f"MCP request failed with code {err.code}: {err.message}"
132138
)
133-
except Exception:
134-
# Fallback if the error doesn't match our schema exactly
139+
else:
135140
raw_error = json_resp.get("error", {})
136141
raise RuntimeError(f"MCP request failed: {raw_error}")
137142

packages/toolbox-core/src/toolbox_core/mcp_transport/v20251125/mcp.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,16 +122,21 @@ async def _send_request(
122122
)
123123
except ValueError:
124124
raise RuntimeError(
125-
f"Invalid state: current protocol {self._protocol_version} is not in supported_protocols."
125+
f"Invalid state: current protocol "\
126+
f"{self._protocol_version} is not in "
127+
f"supported_protocols."
126128
)
127129

128130
try:
129131
err = types.JSONRPCError.model_validate(json_resp).error
132+
except Exception:
133+
err = None
134+
135+
if err:
130136
raise RuntimeError(
131137
f"MCP request failed with code {err.code}: {err.message}"
132138
)
133-
except Exception:
134-
# Fallback if the error doesn't match our schema exactly
139+
else:
135140
raw_error = json_resp.get("error", {})
136141
raise RuntimeError(f"MCP request failed: {raw_error}")
137142

packages/toolbox-core/src/toolbox_core/mcp_transport/v20260618/mcp.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -149,16 +149,21 @@ async def _send_request(
149149
)
150150
except ValueError:
151151
raise RuntimeError(
152-
f"Invalid state: current protocol {self._protocol_version} is not in supported_protocols."
152+
f"Invalid state: current protocol "\
153+
f"{self._protocol_version} is not in "
154+
f"supported_protocols."
153155
)
154156

155157
try:
156158
err = types.JSONRPCError.model_validate(json_resp).error
159+
except Exception:
160+
err = None
161+
162+
if err:
157163
raise RuntimeError(
158164
f"MCP request failed with code {err.code}: {err.message}"
159165
)
160-
except Exception:
161-
# Fallback if the error doesn't match our schema exactly
166+
else:
162167
raw_error = json_resp.get("error", {})
163168
raise RuntimeError(f"MCP request failed: {raw_error}")
164169

packages/toolbox-core/tests/mcp_transport/test_v20241105.py

Lines changed: 44 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -507,57 +507,52 @@ async def test_tool_invoke_split_json_object(self, transport, mocker):
507507
assert result == '{"a": 1}'
508508

509509

510-
@pytest.mark.asyncio
511-
async def test_send_request_400_with_json_rpc_error(
512-
mcp_transport,
513-
):
514-
request = types.MCPRequest(method="some/method", params={"key": "val"})
515-
mock_response = AsyncMock()
516-
mock_response.ok = False
517-
mock_response.status = 400
518-
mock_response.json.return_value = {
519-
"jsonrpc": "2.0",
520-
"id": "test-id",
521-
"error": {"code": -32602, "message": "Missing _meta"},
522-
}
523-
mcp_transport._session.post.return_value.__aenter__.return_value = mock_response
524-
525-
with pytest.raises(
526-
RuntimeError, match="MCP request failed with code -32602: Missing _meta"
527-
):
528-
await mcp_transport._send_request(request, "http://test.local/messages")
510+
async def test_send_request_400_with_json_rpc_error(self, transport):
511+
request = types.MCPRequest(method="some/method", params={"key": "val"})
512+
mock_response = AsyncMock()
513+
mock_response.ok = False
514+
mock_response.status = 400
515+
mock_response.json.return_value = {
516+
"jsonrpc": "2.0",
517+
"id": "test-id",
518+
"error": {"code": -32602, "message": "Missing _meta"},
519+
}
520+
transport._session.post.return_value.__aenter__.return_value = mock_response
529521

522+
with pytest.raises(
523+
RuntimeError, match="MCP request failed with code -32602: Missing _meta"
524+
):
525+
await transport._send_request("http://test.local/messages", request)
530526

531-
@pytest.mark.asyncio
532-
async def test_send_request_400_with_raw_text(mcp_transport):
533-
request = types.MCPRequest(method="some/method", params={"key": "val"})
534-
mock_response = AsyncMock()
535-
mock_response.ok = False
536-
mock_response.status = 400
537-
mock_response.json.side_effect = Exception("Not JSON")
538-
mock_response.text.return_value = "<html>Bad Request</html>"
539-
mcp_transport._session.post.return_value.__aenter__.return_value = mock_response
540527

541-
with pytest.raises(RuntimeError, match="API request failed with status 400"):
542-
await mcp_transport._send_request(request, "http://test.local/messages")
528+
async def test_send_request_400_with_raw_text(self, transport):
529+
request = types.MCPRequest(method="some/method", params={"key": "val"})
530+
mock_response = AsyncMock()
531+
mock_response.ok = False
532+
mock_response.status = 400
533+
mock_response.json.side_effect = Exception("Not JSON")
534+
mock_response.text.return_value = "<html>Bad Request</html>"
535+
transport._session.post.return_value.__aenter__.return_value = mock_response
543536

537+
with pytest.raises(RuntimeError, match="API request failed with status 400"):
538+
await transport._send_request("http://test.local/messages", request)
544539

545-
@pytest.mark.asyncio
546-
async def test_version_negotiation_legacy_string_fallback(
547-
mcp_transport,
548-
):
549-
from aioresponses import aioresponses
550-
551-
request = types.MCPRequest(method="some/method", params={})
552-
with aioresponses() as m:
553-
m.post(
554-
"http://test.local/messages",
555-
status=400,
556-
payload={
557-
"jsonrpc": "2.0",
558-
"id": "test-id",
559-
"error": "invalid protocol version",
560-
},
561-
)
562-
with pytest.raises(ProtocolNegotiationError):
563-
await mcp_transport._send_request(request, "http://test.local/messages")
540+
541+
async def test_version_negotiation_legacy_string_fallback(self, transport):
542+
from toolbox_core.exceptions import ProtocolNegotiationError
543+
544+
request = types.MCPRequest(method="some/method", params={})
545+
mock_response_reject = AsyncMock()
546+
mock_response_reject.ok = False
547+
mock_response_reject.status = 400
548+
mock_response_reject.json.return_value = {
549+
"jsonrpc": "2.0",
550+
"id": "test-id",
551+
"error": "invalid protocol version",
552+
}
553+
554+
mock_post = transport._session.post.return_value
555+
mock_post.__aenter__.return_value = mock_response_reject
556+
557+
with pytest.raises(RuntimeError, match="no fallback versions"):
558+
await transport._send_request("http://test.local/messages", request)

packages/toolbox-core/tests/mcp_transport/test_v20250326.py

Lines changed: 43 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -505,57 +505,52 @@ async def test_tool_invoke_split_json_object(self, transport, mocker):
505505
assert result == '{"a": 1}'
506506

507507

508-
@pytest.mark.asyncio
509-
async def test_send_request_400_with_json_rpc_error(
510-
mcp_transport,
511-
):
512-
request = types.MCPRequest(method="some/method", params={"key": "val"})
513-
mock_response = AsyncMock()
514-
mock_response.ok = False
515-
mock_response.status = 400
516-
mock_response.json.return_value = {
517-
"jsonrpc": "2.0",
518-
"id": "test-id",
519-
"error": {"code": -32602, "message": "Missing _meta"},
520-
}
521-
mcp_transport._session.post.return_value.__aenter__.return_value = mock_response
522-
523-
with pytest.raises(
524-
RuntimeError, match="MCP request failed with code -32602: Missing _meta"
525-
):
526-
await mcp_transport._send_request(request, "http://test.local/messages")
508+
async def test_send_request_400_with_json_rpc_error(self, transport):
509+
request = types.MCPRequest(method="some/method", params={"key": "val"})
510+
mock_response = AsyncMock()
511+
mock_response.ok = False
512+
mock_response.status = 400
513+
mock_response.json.return_value = {
514+
"jsonrpc": "2.0",
515+
"id": "test-id",
516+
"error": {"code": -32602, "message": "Missing _meta"},
517+
}
518+
transport._session.post.return_value.__aenter__.return_value = mock_response
519+
520+
with pytest.raises(
521+
RuntimeError, match="MCP request failed with code -32602: Missing _meta"
522+
):
523+
await transport._send_request("http://test.local/messages", request)
527524

528525

529-
@pytest.mark.asyncio
530-
async def test_send_request_400_with_raw_text(mcp_transport):
531-
request = types.MCPRequest(method="some/method", params={"key": "val"})
532-
mock_response = AsyncMock()
533-
mock_response.ok = False
534-
mock_response.status = 400
535-
mock_response.json.side_effect = Exception("Not JSON")
536-
mock_response.text.return_value = "<html>Bad Request</html>"
537-
mcp_transport._session.post.return_value.__aenter__.return_value = mock_response
526+
async def test_send_request_400_with_raw_text(self, transport):
527+
request = types.MCPRequest(method="some/method", params={"key": "val"})
528+
mock_response = AsyncMock()
529+
mock_response.ok = False
530+
mock_response.status = 400
531+
mock_response.json.side_effect = Exception("Not JSON")
532+
mock_response.text.return_value = "<html>Bad Request</html>"
533+
transport._session.post.return_value.__aenter__.return_value = mock_response
538534

539-
with pytest.raises(RuntimeError, match="API request failed with status 400"):
540-
await mcp_transport._send_request(request, "http://test.local/messages")
535+
with pytest.raises(RuntimeError, match="API request failed with status 400"):
536+
await transport._send_request("http://test.local/messages", request)
541537

542538

543-
@pytest.mark.asyncio
544-
async def test_version_negotiation_legacy_string_fallback(
545-
mcp_transport,
546-
):
547-
from aioresponses import aioresponses
548-
549-
request = types.MCPRequest(method="some/method", params={})
550-
with aioresponses() as m:
551-
m.post(
552-
"http://test.local/messages",
553-
status=400,
554-
payload={
555-
"jsonrpc": "2.0",
556-
"id": "test-id",
557-
"error": "invalid protocol version",
558-
},
559-
)
539+
async def test_version_negotiation_legacy_string_fallback(self, transport):
540+
from toolbox_core.exceptions import ProtocolNegotiationError
541+
542+
request = types.MCPRequest(method="some/method", params={})
543+
mock_response_reject = AsyncMock()
544+
mock_response_reject.ok = False
545+
mock_response_reject.status = 400
546+
mock_response_reject.json.return_value = {
547+
"jsonrpc": "2.0",
548+
"id": "test-id",
549+
"error": "invalid protocol version",
550+
}
551+
552+
mock_post = transport._session.post.return_value
553+
mock_post.__aenter__.return_value = mock_response_reject
554+
560555
with pytest.raises(ProtocolNegotiationError):
561-
await mcp_transport._send_request(request, "http://test.local/messages")
556+
await transport._send_request("http://test.local/messages", request)

0 commit comments

Comments
 (0)