test: align MCP transient reconnect coverage

maintainer edit: update the existing transient retry tests for reconnect-first behavior and keep structured retry-failure coverage in the focused MCP transient suite.
This commit is contained in:
chengyongru
2026-07-04 21:15:18 +08:00
committed by Xubin Ren
parent 6d28db3248
commit 614ea86a81
2 changed files with 8 additions and 62 deletions
+8 -7
View File
@@ -16,6 +16,7 @@ from nanobot.agent.tools.mcp import (
_is_session_terminated,
_is_transient,
)
from nanobot.agent.tools.registry import is_tool_error_result
# ---------------------------------------------------------------------------
# _is_transient helper
@@ -134,6 +135,7 @@ async def test_tool_fails_after_retry_exhausted():
assert "failed after retry" in output
assert "ClosedResourceError" in output
assert is_tool_error_result(wrapper.name, output)
assert session.call_tool.call_count == 2
@@ -237,12 +239,10 @@ async def test_tool_retry_on_end_of_stream():
@pytest.mark.asyncio
async def test_tool_reconnects_when_transient_retry_reveals_terminated_session():
"""Tool should reconnect if a stale session reports termination after transient retry."""
async def test_tool_reconnects_on_transient_failure():
"""Tool should reconnect when a stale session reports a transient stream failure."""
old_session = AsyncMock()
old_session.call_tool = AsyncMock(
side_effect=[_FakeClosedResourceError("closed"), _session_terminated_error()]
)
old_session.call_tool = AsyncMock(side_effect=_FakeClosedResourceError("closed"))
new_session = AsyncMock()
new_session.call_tool = AsyncMock(return_value=_make_tool_result("fresh"))
@@ -257,12 +257,13 @@ async def test_tool_reconnects_when_transient_retry_reveals_terminated_session()
wrapper.set_reconnect_handler(reconnect)
with patch("nanobot.agent.tools.mcp.asyncio.sleep", new_callable=AsyncMock):
with patch("nanobot.agent.tools.mcp.asyncio.sleep", new_callable=AsyncMock) as mock_sleep:
output = await wrapper.execute(foo="bar")
assert output == "fresh"
assert old_session.call_tool.call_count == 2
assert old_session.call_tool.call_count == 1
assert new_session.call_tool.call_count == 1
mock_sleep.assert_not_called()
# ---------------------------------------------------------------------------
-55
View File
@@ -478,61 +478,6 @@ async def test_execute_handles_generic_exception() -> None:
assert is_tool_error_result(wrapper.name, result)
@pytest.mark.asyncio
async def test_execute_reconnects_on_transient_failure() -> None:
class ClosedResourceError(Exception):
pass
reconnects = 0
async def stale_call_tool(_name: str, arguments: dict) -> object:
raise ClosedResourceError("stream closed")
async def fresh_call_tool(_name: str, arguments: dict) -> object:
return SimpleNamespace(content=[_FakeTextContent("ok")])
wrapper = _make_wrapper(SimpleNamespace(call_tool=stale_call_tool))
async def reconnect(_server_name: str, _tool_name: str, _stale_tool: object) -> object:
nonlocal reconnects
reconnects += 1
return SimpleNamespace(_session=SimpleNamespace(call_tool=fresh_call_tool))
wrapper.set_reconnect_handler(reconnect)
result = await wrapper.execute()
assert result == "ok"
assert reconnects == 1
@pytest.mark.asyncio
async def test_execute_marks_transient_retry_failure_as_tool_error(
monkeypatch: pytest.MonkeyPatch,
) -> None:
class ClosedResourceError(Exception):
pass
calls = 0
async def call_tool(_name: str, arguments: dict) -> object:
nonlocal calls
calls += 1
raise ClosedResourceError("closed")
async def fast_sleep(_delay: float) -> None:
return None
monkeypatch.setattr(mcp_mod.asyncio, "sleep", fast_sleep)
wrapper = _make_wrapper(SimpleNamespace(call_tool=call_tool))
result = await wrapper.execute()
assert calls == 2
assert result == "(MCP tool call failed after retry: ClosedResourceError)"
assert is_tool_error_result(wrapper.name, result)
def _make_tool_def(name: str) -> SimpleNamespace:
return SimpleNamespace(
name=name,