Skip to content

Commit 2021434

Browse files
author
Jay Hemnani
committed
test: fix coverage for SSE disconnect without resumption token
Rewrite test to actually call _handle_sse_response() instead of duplicating the implementation logic inline. This ensures lines 440-451 in streamable_http.py are properly covered. The test mocks EventSource to yield no events, simulating a stream that closes immediately without sending any resumption tokens. Github-Issue: #1811
1 parent 8c703a5 commit 2021434

File tree

1 file changed

+24
-23
lines changed

1 file changed

+24
-23
lines changed

tests/shared/test_streamable_http.py

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2403,11 +2403,13 @@ async def test_sse_disconnect_without_resumption_token_sends_error() -> None:
24032403
any events with IDs (e.g., timeout fires before server sends response), the
24042404
client should send a JSONRPCError to the session layer instead of hanging forever.
24052405
2406-
This test verifies the else branch in _handle_sse_response() correctly sends
2407-
an error to the session layer when last_event_id is None after stream disconnect.
2406+
This test exercises the else branch in _handle_sse_response() by mocking
2407+
EventSource to yield no events, simulating a stream that closes immediately.
24082408
"""
2409-
from mcp.client.streamable_http import RequestContext
2410-
from mcp.types import ErrorData, JSONRPCError, JSONRPCMessage, JSONRPCRequest
2409+
from unittest.mock import patch
2410+
2411+
from mcp.client.streamable_http import RequestContext, StreamableHTTPTransport
2412+
from mcp.types import CONNECTION_CLOSED, JSONRPCError, JSONRPCMessage, JSONRPCRequest
24112413

24122414
# Create a mock request (needed for the else branch to extract request_id)
24132415
mock_request = JSONRPCRequest(jsonrpc="2.0", id="test-request-123", method="tools/call")
@@ -2417,7 +2419,7 @@ async def test_sse_disconnect_without_resumption_token_sends_error() -> None:
24172419
# Create memory streams for the test
24182420
read_stream_writer, read_stream = anyio.create_memory_object_stream[SessionMessage | Exception](1)
24192421

2420-
# Create a mock httpx client (not used in the else branch path)
2422+
# Create a mock httpx client
24212423
mock_client = MagicMock()
24222424

24232425
# Create the request context
@@ -2429,30 +2431,29 @@ async def test_sse_disconnect_without_resumption_token_sends_error() -> None:
24292431
read_stream_writer=read_stream_writer,
24302432
)
24312433

2432-
# Simulate what happens in _handle_sse_response when stream ends without events:
2433-
# The else branch should send an error to read_stream_writer
2434-
last_event_id = None # Simulating no events received before disconnect
2435-
2436-
# This is the code path we're testing (from the else branch in _handle_sse_response)
2437-
if last_event_id is None:
2438-
if isinstance(ctx.session_message.message.root, JSONRPCRequest):
2439-
request_id = ctx.session_message.message.root.id
2440-
error_response = JSONRPCError(
2441-
jsonrpc="2.0",
2442-
id=request_id,
2443-
error=ErrorData(
2444-
code=-32000,
2445-
message="SSE stream disconnected before receiving response",
2446-
),
2447-
)
2448-
await ctx.read_stream_writer.send(SessionMessage(JSONRPCMessage(error_response)))
2434+
# Create a mock response
2435+
mock_response = MagicMock(spec=httpx.Response)
2436+
2437+
# Create transport instance
2438+
transport = StreamableHTTPTransport("http://test.example/mcp")
2439+
2440+
# Mock EventSource to yield nothing (simulates stream closing immediately without events)
2441+
async def empty_aiter_sse():
2442+
return
2443+
yield # Makes this an async generator that yields nothing
2444+
2445+
with patch("mcp.client.streamable_http.EventSource") as mock_event_source:
2446+
mock_event_source.return_value.aiter_sse = empty_aiter_sse
2447+
2448+
# Call the actual method - this exercises lines 437-451 in streamable_http.py
2449+
await transport._handle_sse_response(mock_response, ctx)
24492450

24502451
# Verify an error was sent to the stream
24512452
received = await read_stream.receive()
24522453
assert isinstance(received, SessionMessage)
24532454
assert isinstance(received.message.root, JSONRPCError)
24542455
assert received.message.root.id == "test-request-123"
2455-
assert received.message.root.error.code == -32000
2456+
assert received.message.root.error.code == CONNECTION_CLOSED
24562457
assert "SSE stream disconnected" in received.message.root.error.message
24572458

24582459
# Cleanup

0 commit comments

Comments
 (0)