fix(gateway): make resource teardown cancellation-safe

This commit is contained in:
Xubin Ren
2026-08-03 16:00:39 +08:00
parent a91ce900ef
commit 39e1533c3b
4 changed files with 118 additions and 24 deletions
+56
View File
@@ -55,6 +55,62 @@ class TestHandleStop:
out = await cmd_stop(ctx)
assert "No active task" in out.content
@pytest.mark.asyncio
async def test_close_mcp_cancels_active_turn_before_resources(self):
loop, _bus = _make_loop()
events: list[str] = []
async def active_turn():
try:
await asyncio.sleep(60)
except asyncio.CancelledError:
events.append("turn_cancelled")
raise
task = asyncio.create_task(active_turn())
await asyncio.sleep(0)
loop._active_tasks["test:c1"] = {task}
async def close_subagents():
events.append("resources_closed")
loop.subagents.close = close_subagents
loop._exec_session_manager.close_all = AsyncMock()
with patch("nanobot.agent.loop.agent_context.close_mcp", AsyncMock()):
await loop.close_mcp()
assert events == ["turn_cancelled", "resources_closed"]
assert task.cancelled()
@pytest.mark.asyncio
async def test_close_mcp_serializes_duplicate_cleanup(self):
loop, _bus = _make_loop()
entered = asyncio.Event()
release = asyncio.Event()
concurrent = 0
max_concurrent = 0
async def close_subagents():
nonlocal concurrent, max_concurrent
concurrent += 1
max_concurrent = max(max_concurrent, concurrent)
entered.set()
await release.wait()
concurrent -= 1
loop.subagents.close = close_subagents
loop._exec_session_manager.close_all = AsyncMock()
with patch("nanobot.agent.loop.agent_context.close_mcp", AsyncMock()):
first = asyncio.create_task(loop.close_mcp())
await entered.wait()
second = asyncio.create_task(loop.close_mcp())
await asyncio.sleep(0)
assert not second.done()
release.set()
await asyncio.gather(first, second)
assert max_concurrent == 1
@pytest.mark.asyncio
async def test_stop_cancels_active_task(self):
from nanobot.bus.events import InboundMessage