mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-07-14 12:48:03 +00:00
fix(mcp_manager): implement concurrent server connections with timeout handling
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
import asyncio
|
||||
import json
|
||||
import time
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
class FakeQuery:
|
||||
def __init__(self, servers):
|
||||
self._servers = servers
|
||||
|
||||
def filter(self, *_args, **_kwargs):
|
||||
return self
|
||||
|
||||
def all(self):
|
||||
return self._servers
|
||||
|
||||
|
||||
class FakeDB:
|
||||
def __init__(self, servers):
|
||||
self._servers = servers
|
||||
|
||||
def query(self, *_args, **_kwargs):
|
||||
return FakeQuery(self._servers)
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_connect_all_enabled_runs_concurrently(monkeypatch):
|
||||
from src.mcp_manager import McpManager
|
||||
|
||||
manager = McpManager()
|
||||
|
||||
servers = [
|
||||
SimpleNamespace(
|
||||
id=1,
|
||||
name="server1",
|
||||
transport="stdio",
|
||||
command="cmd1",
|
||||
args=json.dumps([]),
|
||||
env=json.dumps({}),
|
||||
url=None,
|
||||
),
|
||||
SimpleNamespace(
|
||||
id=2,
|
||||
name="server2",
|
||||
transport="stdio",
|
||||
command="cmd2",
|
||||
args=json.dumps([]),
|
||||
env=json.dumps({}),
|
||||
url=None,
|
||||
),
|
||||
SimpleNamespace(
|
||||
id=3,
|
||||
name="server3",
|
||||
transport="stdio",
|
||||
command="cmd3",
|
||||
args=json.dumps([]),
|
||||
env=json.dumps({}),
|
||||
url=None,
|
||||
),
|
||||
]
|
||||
|
||||
# Patch the SessionLocal used by connect_all_enabled().
|
||||
import src.mcp_manager as mcp_manager
|
||||
|
||||
monkeypatch.setattr(
|
||||
mcp_manager,
|
||||
"SessionLocal",
|
||||
lambda: FakeDB(servers),
|
||||
)
|
||||
|
||||
async def fake_connect_with_timeout(_server):
|
||||
await asyncio.sleep(1)
|
||||
|
||||
# We're testing that connect_all_enabled launches these concurrently,
|
||||
# not the implementation of connect_server().
|
||||
monkeypatch.setattr(
|
||||
manager,
|
||||
"_connect_with_timeout",
|
||||
fake_connect_with_timeout,
|
||||
)
|
||||
|
||||
start = time.perf_counter()
|
||||
|
||||
await manager.connect_all_enabled()
|
||||
|
||||
elapsed = time.perf_counter() - start
|
||||
|
||||
# Sequential would take ~3 seconds.
|
||||
# Concurrent should take about 1 second.
|
||||
assert 0.9 <= elapsed < 2.0
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_connect_all_enabled_timeout_does_not_block_other_servers(monkeypatch):
|
||||
import src.mcp_manager as mcp_manager
|
||||
from src.mcp_manager import McpManager
|
||||
|
||||
manager = McpManager()
|
||||
|
||||
servers = [
|
||||
SimpleNamespace(
|
||||
id=1,
|
||||
name="fast1",
|
||||
transport="stdio",
|
||||
command="cmd1",
|
||||
args=json.dumps([]),
|
||||
env=json.dumps({}),
|
||||
url=None,
|
||||
),
|
||||
SimpleNamespace(
|
||||
id=2,
|
||||
name="slow",
|
||||
transport="stdio",
|
||||
command="cmd2",
|
||||
args=json.dumps([]),
|
||||
env=json.dumps({}),
|
||||
url=None,
|
||||
),
|
||||
SimpleNamespace(
|
||||
id=3,
|
||||
name="fast2",
|
||||
transport="stdio",
|
||||
command="cmd3",
|
||||
args=json.dumps([]),
|
||||
env=json.dumps({}),
|
||||
url=None,
|
||||
),
|
||||
]
|
||||
|
||||
monkeypatch.setattr(
|
||||
mcp_manager,
|
||||
"SessionLocal",
|
||||
lambda: FakeDB(servers),
|
||||
)
|
||||
|
||||
completed = []
|
||||
|
||||
async def fake_connect_server(server_id, **kwargs):
|
||||
if server_id == 2:
|
||||
# Simulate a hung connection.
|
||||
await asyncio.sleep(30)
|
||||
else:
|
||||
await asyncio.sleep(0.1)
|
||||
completed.append(server_id)
|
||||
|
||||
monkeypatch.setattr(
|
||||
manager,
|
||||
"connect_server",
|
||||
fake_connect_server,
|
||||
)
|
||||
|
||||
#
|
||||
# Don't actually wait 20 seconds during the test.
|
||||
# Replace asyncio.wait_for used by mcp_manager with a much shorter timeout.
|
||||
#
|
||||
real_wait_for = asyncio.wait_for
|
||||
|
||||
async def short_wait_for(awaitable, timeout):
|
||||
return await real_wait_for(awaitable, timeout=0.2)
|
||||
|
||||
monkeypatch.setattr(
|
||||
mcp_manager.asyncio,
|
||||
"wait_for",
|
||||
short_wait_for,
|
||||
)
|
||||
|
||||
start = time.perf_counter()
|
||||
|
||||
await manager.connect_all_enabled()
|
||||
|
||||
elapsed = time.perf_counter() - start
|
||||
|
||||
assert set(completed) == {1, 3}
|
||||
assert elapsed < 1
|
||||
Reference in New Issue
Block a user