Skip to content

Commit 1703d42

Browse files
committed
Make the late-response-ignored pin falsifiable
The previous canary (a raising message_handler) could never fire: correct code drops unknown-id responses before any handler, and regressed code's delivery paths contain handler exceptions. Collect surfaced messages instead and assert only a control notification arrives; verified against a simulation of the v1 surface-as-RuntimeError behavior.
1 parent 2d33ade commit 1703d42

1 file changed

Lines changed: 16 additions & 2 deletions

File tree

tests/interaction/lowlevel/test_cancellation.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,11 @@ async def test_a_response_for_an_unknown_request_id_is_ignored() -> None:
217217
that is the same client-side code path as any response with an unknown id, and that form is
218218
deterministic to test without a client-side cancellation API.
219219
220+
"Ignored" is proved in two halves: the pong round-trip proves the read loop survived the
221+
fabricated response (the ordered in-memory stream routed it first), and `surfaced` holding
222+
only the control notification proves the fabricated response was never delivered to
223+
`message_handler` (v1 surfaced it there as a RuntimeError).
224+
220225
A real Server cannot be made to answer with a fabricated id, so the test plays the server's
221226
side of the wire by hand. Reserve this pattern for behaviour no real server can produce. The
222227
other tests in this file run over the transport matrix; this one is in-memory only because the
@@ -261,12 +266,18 @@ def respond(request_id: types.RequestId, result: types.Result) -> SessionMessage
261266
assert isinstance(ping, SessionMessage)
262267
assert isinstance(ping.message, JSONRPCRequest)
263268
assert ping.message.method == "ping"
264-
# First answer with a fabricated id that matches nothing in flight, then the real id.
269+
# First a fabricated id that matches nothing in flight, then a control notification that
270+
# is surfaced to message_handler (proving the handler is live), then the real id.
265271
await server_write.send(respond(9999, EmptyResult()))
272+
await server_write.send(
273+
SessionMessage(JSONRPCNotification(jsonrpc="2.0", method="notifications/tools/list_changed"))
274+
)
266275
await server_write.send(respond(ping.message.id, EmptyResult()))
267276

277+
surfaced: list[IncomingMessage] = []
278+
268279
async def message_handler(message: IncomingMessage) -> None:
269-
raise NotImplementedError # unreachable: nothing is surfaced for an unknown-id response
280+
surfaced.append(message)
270281

271282
async with (
272283
create_client_server_memory_streams() as ((client_read, client_write), server_streams),
@@ -279,6 +290,9 @@ async def message_handler(message: IncomingMessage) -> None:
279290
pong = await session.send_request(PingRequest(), EmptyResult)
280291

281292
assert pong == snapshot(EmptyResult())
293+
# The stream is ordered, so the fabricated response was routed before the control
294+
# notification: only the control surfaced, so the unknown-id response was dropped.
295+
assert surfaced == snapshot([types.ToolListChangedNotification()])
282296

283297

284298
@requirement("protocol:cancel:initialize-not-cancellable")

0 commit comments

Comments
 (0)