fix(async): offload synchronous plugin methods

This commit is contained in:
InfinityPacer
2026-08-23 15:19:15 +08:00
parent 1f64e99a6f
commit 2ac61337b8
6 changed files with 196 additions and 9 deletions
@@ -0,0 +1,71 @@
"""插件管理器异步方法的执行边界回归。"""
from types import SimpleNamespace
from typing import Iterator
from unittest.mock import AsyncMock
import pytest
from app.foundation.singleton import Singleton
from app.sdk.plugins import PluginManager
@pytest.fixture
def plugin_manager() -> Iterator[PluginManager]:
"""构造隔离的插件管理器实例。"""
Singleton._instances.pop((PluginManager, (), frozenset()), None)
manager = PluginManager()
yield manager
Singleton._instances.pop((PluginManager, (), frozenset()), None)
@pytest.mark.asyncio
async def test_async_run_plugin_method_offloads_sync_plugin_method(
plugin_manager: PluginManager,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""异步入口不得在事件循环内直接执行同步插件方法。"""
calls: list[tuple[str, int]] = []
def sync_method(value: int) -> int:
calls.append(("sync", value))
return value + 1
plugin_manager.running_plugins["DemoPlugin"] = SimpleNamespace(
sync_method=sync_method,
)
worker = AsyncMock(return_value=2)
monkeypatch.setattr(
"app.runtime.extensions.plugin_manager.run_in_threadpool_to_completion",
worker,
)
assert await plugin_manager.async_run_plugin_method(
"DemoPlugin", "sync_method", 1
) == 2
worker.assert_awaited_once_with(sync_method, 1)
assert calls == []
@pytest.mark.asyncio
async def test_async_run_plugin_method_keeps_async_plugin_method_on_loop(
plugin_manager: PluginManager,
monkeypatch: pytest.MonkeyPatch,
) -> None:
"""原生协程插件方法继续直接等待,不额外占用同步 worker。"""
async def async_method(value: int) -> int:
return value + 1
plugin_manager.running_plugins["DemoPlugin"] = SimpleNamespace(
async_method=async_method,
)
worker = AsyncMock()
monkeypatch.setattr(
"app.runtime.extensions.plugin_manager.run_in_threadpool_to_completion",
worker,
)
assert await plugin_manager.async_run_plugin_method(
"DemoPlugin", "async_method", 1
) == 2
worker.assert_not_awaited()