|
| 1 | +import asyncio |
1 | 2 | import logging
|
2 | 3 | import os
|
| 4 | +import threading |
| 5 | +from typing import cast |
3 | 6 | from unittest.mock import AsyncMock, MagicMock
|
4 | 7 |
|
5 | 8 | import pytest
|
| 9 | +from _pytest.logging import LogCaptureFixture # type: ignore[import] |
6 | 10 | from autogen_core import CancellationToken
|
7 | 11 | from autogen_core.tools import Workbench
|
8 | 12 | from autogen_core.utils import schema_to_pydantic_model
|
9 | 13 | from autogen_ext.tools.mcp import (
|
| 14 | + McpSessionActor, |
10 | 15 | McpWorkbench,
|
11 | 16 | SseMcpToolAdapter,
|
12 | 17 | SseServerParams,
|
@@ -594,4 +599,54 @@ async def test_lazy_init_and_finalize_cleanup() -> None:
|
594 | 599 | assert workbench._actor is not None # type: ignore[reportPrivateUsage]
|
595 | 600 | assert workbench._actor._active is True # type: ignore[reportPrivateUsage]
|
596 | 601 |
|
| 602 | + actor = workbench._actor # type: ignore[reportPrivateUsage] |
597 | 603 | del workbench
|
| 604 | + await asyncio.sleep(0.1) |
| 605 | + assert actor._active is False |
| 606 | + |
| 607 | + |
| 608 | +@pytest.mark.asyncio |
| 609 | +async def test_del_to_new_event_loop_when_get_event_loop_fails() -> None: |
| 610 | + params = StdioServerParams( |
| 611 | + command="npx", |
| 612 | + args=[ |
| 613 | + "-y", |
| 614 | + "@modelcontextprotocol/server-filesystem", |
| 615 | + ".", |
| 616 | + ], |
| 617 | + read_timeout_seconds=60, |
| 618 | + ) |
| 619 | + workbench = McpWorkbench(server_params=params) |
| 620 | + |
| 621 | + await workbench.list_tools() |
| 622 | + assert workbench._actor is not None # type: ignore[reportPrivateUsage] |
| 623 | + assert workbench._actor._active is True # type: ignore[reportPrivateUsage] |
| 624 | + |
| 625 | + actor = workbench._actor # type: ignore[reportPrivateUsage] |
| 626 | + |
| 627 | + def cleanup() -> None: |
| 628 | + nonlocal workbench |
| 629 | + del workbench |
| 630 | + |
| 631 | + t = threading.Thread(target=cleanup) |
| 632 | + t.start() |
| 633 | + t.join() |
| 634 | + |
| 635 | + await asyncio.sleep(0.1) |
| 636 | + assert actor._active is False # type: ignore[reportPrivateUsage] |
| 637 | + |
| 638 | + |
| 639 | +def test_del_raises_when_loop_closed() -> None: |
| 640 | + loop = asyncio.new_event_loop() |
| 641 | + asyncio.set_event_loop(loop) |
| 642 | + |
| 643 | + params = StdioServerParams(command="echo", args=["ok"]) |
| 644 | + workbench = McpWorkbench(server_params=params) |
| 645 | + |
| 646 | + workbench._actor_loop = loop # type: ignore[reportPrivateUsage] |
| 647 | + workbench._actor = cast(McpSessionActor, object()) # type: ignore[reportPrivateUsage] |
| 648 | + |
| 649 | + loop.close() |
| 650 | + |
| 651 | + with pytest.warns(RuntimeWarning, match="loop is closed or not running"): |
| 652 | + del workbench |
0 commit comments