From 49026931324423ad5ac3a41f15105d4af0121b1b Mon Sep 17 00:00:00 2001 From: Max Isbey <224885523+maxisbey@users.noreply.github.com> Date: Tue, 28 Oct 2025 18:11:11 +0000 Subject: [PATCH] fix: eliminate flaky timeout test by using per-request timeouts The test was using a session-level 50ms timeout which caused race conditions in CI environments where operations could occasionally take longer than expected due to system load. The fix uses per-request timeouts instead: - Fast operations use no timeout (None), making them reliable in any environment regardless of system load - The slow operation that should timeout uses a minimal timeout (1 microsecond) to trigger immediately without adding test execution time This eliminates time-based race conditions while keeping the test fast and maintaining its purpose of verifying that the server remains responsive after a timeout occurs. --- tests/issues/test_88_random_error.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/tests/issues/test_88_random_error.py b/tests/issues/test_88_random_error.py index 5584abcaea..8ed92ba53d 100644 --- a/tests/issues/test_88_random_error.py +++ b/tests/issues/test_88_random_error.py @@ -27,6 +27,10 @@ async def test_notification_validation_error(tmp_path: Path): 2. The server can still handle new requests 3. The client can make new requests 4. No resources are leaked + + Uses per-request timeouts to avoid race conditions: + - Fast operations use no timeout (reliable in any environment) + - Slow operations use minimal timeout (10ms) for quick test execution """ server = Server(name="test") @@ -79,29 +83,29 @@ async def client( write_stream: MemoryObjectSendStream[SessionMessage], scope: anyio.CancelScope, ): - # Use a timeout that's: - # - Long enough for fast operations (>10ms) - # - Short enough for slow operations (<200ms) - # - Not too short to avoid flakiness - async with ClientSession(read_stream, write_stream, read_timeout_seconds=timedelta(milliseconds=50)) as session: + # No session-level timeout to avoid race conditions with fast operations + async with ClientSession(read_stream, write_stream) as session: await session.initialize() - # First call should work (fast operation) - result = await session.call_tool("fast") + # First call should work (fast operation, no timeout) + result = await session.call_tool("fast", read_timeout_seconds=None) assert result.content == [TextContent(type="text", text="fast 1")] assert not slow_request_lock.is_set() - # Second call should timeout (slow operation) + # Second call should timeout (slow operation with minimal timeout) + # Use 10ms timeout to trigger quickly without waiting with pytest.raises(McpError) as exc_info: - await session.call_tool("slow") + await session.call_tool( + "slow", read_timeout_seconds=timedelta(microseconds=1) + ) # artificial timeout that always fails assert "Timed out while waiting" in str(exc_info.value) # release the slow request not to have hanging process slow_request_lock.set() - # Third call should work (fast operation), + # Third call should work (fast operation, no timeout), # proving server is still responsive - result = await session.call_tool("fast") + result = await session.call_tool("fast", read_timeout_seconds=None) assert result.content == [TextContent(type="text", text="fast 3")] scope.cancel()