- Add test for fallback to bot account in platform IO route metadata when context message is absent. - Improve PlatformIOManager to avoid duplicate driver entries and streamline fallback driver handling. - Refactor OutboundTracker to support tracking by both internal message ID and driver ID, enhancing the uniqueness of pending records. - Introduce dynamic API capabilities in RuntimeComponent, allowing plugins to replace their dynamic API lists. - Update APIRegistry to manage dynamic APIs more effectively, including registration and toggling of API statuses. - Implement authorization checks for dynamic API capabilities to ensure proper permissions. - Restrict direct calls to certain host RPC methods from plugins for enhanced security. - Refactor send_service to ensure fallback to current platform account when no context message is available.
179 lines
5.8 KiB
Python
179 lines
5.8 KiB
Python
"""Platform IO legacy driver 回归测试。"""
|
|
|
|
from typing import Any, Dict, Optional
|
|
|
|
import pytest
|
|
|
|
from src.chat.utils import utils as chat_utils
|
|
from src.chat.message_receive import uni_message_sender
|
|
from src.platform_io.drivers.base import PlatformIODriver
|
|
from src.platform_io.drivers.legacy_driver import LegacyPlatformDriver
|
|
from src.platform_io.manager import PlatformIOManager
|
|
from src.platform_io.types import DeliveryReceipt, DeliveryStatus, DriverDescriptor, DriverKind, RouteBinding, RouteKey
|
|
|
|
|
|
class _PluginDriver(PlatformIODriver):
|
|
"""测试用插件发送驱动。"""
|
|
|
|
def __init__(self, driver_id: str, platform: str) -> None:
|
|
"""初始化测试驱动。
|
|
|
|
Args:
|
|
driver_id: 驱动 ID。
|
|
platform: 负责的平台名称。
|
|
"""
|
|
super().__init__(
|
|
DriverDescriptor(
|
|
driver_id=driver_id,
|
|
kind=DriverKind.PLUGIN,
|
|
platform=platform,
|
|
plugin_id="test.plugin",
|
|
)
|
|
)
|
|
|
|
async def send_message(
|
|
self,
|
|
message: Any,
|
|
route_key: RouteKey,
|
|
metadata: Optional[Dict[str, Any]] = None,
|
|
) -> DeliveryReceipt:
|
|
"""返回一个固定成功回执。
|
|
|
|
Args:
|
|
message: 待发送消息。
|
|
route_key: 当前路由键。
|
|
metadata: 发送元数据。
|
|
|
|
Returns:
|
|
DeliveryReceipt: 固定成功回执。
|
|
"""
|
|
del metadata
|
|
return DeliveryReceipt(
|
|
internal_message_id=str(message.message_id),
|
|
route_key=route_key,
|
|
status=DeliveryStatus.SENT,
|
|
driver_id=self.driver_id,
|
|
driver_kind=self.descriptor.kind,
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_platform_io_uses_legacy_driver_when_no_explicit_send_route(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""没有显式发送路由时,应由 Platform IO 回退到 legacy driver。"""
|
|
manager = PlatformIOManager()
|
|
monkeypatch.setattr(chat_utils, "get_all_bot_accounts", lambda: {"qq": "bot-qq"})
|
|
|
|
try:
|
|
await manager.ensure_send_pipeline_ready()
|
|
|
|
fallback_drivers = manager.resolve_drivers(RouteKey(platform="qq"))
|
|
assert [driver.driver_id for driver in fallback_drivers] == ["legacy.send.qq"]
|
|
|
|
plugin_driver = _PluginDriver(driver_id="plugin.qq.sender", platform="qq")
|
|
await manager.add_driver(plugin_driver)
|
|
manager.bind_send_route(
|
|
RouteBinding(
|
|
route_key=RouteKey(platform="qq"),
|
|
driver_id=plugin_driver.driver_id,
|
|
driver_kind=plugin_driver.descriptor.kind,
|
|
)
|
|
)
|
|
|
|
explicit_drivers = manager.resolve_drivers(RouteKey(platform="qq"))
|
|
assert [driver.driver_id for driver in explicit_drivers] == ["plugin.qq.sender", "legacy.send.qq"]
|
|
finally:
|
|
await manager.stop()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_platform_io_broadcasts_to_plugin_and_legacy_driver(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""同一路由命中插件驱动与 legacy driver 时,应同时广播发送。"""
|
|
|
|
manager = PlatformIOManager()
|
|
legacy_calls: list[dict[str, Any]] = []
|
|
monkeypatch.setattr(chat_utils, "get_all_bot_accounts", lambda: {"qq": "bot-qq"})
|
|
|
|
async def _fake_send_prepared_message_to_platform(message: Any, show_log: bool = True) -> bool:
|
|
"""记录 legacy driver 调用。"""
|
|
|
|
legacy_calls.append({"message": message, "show_log": show_log})
|
|
return True
|
|
|
|
monkeypatch.setattr(
|
|
uni_message_sender,
|
|
"send_prepared_message_to_platform",
|
|
_fake_send_prepared_message_to_platform,
|
|
)
|
|
|
|
try:
|
|
await manager.ensure_send_pipeline_ready()
|
|
|
|
plugin_driver = _PluginDriver(driver_id="plugin.qq.sender", platform="qq")
|
|
await manager.add_driver(plugin_driver)
|
|
manager.bind_send_route(
|
|
RouteBinding(
|
|
route_key=RouteKey(platform="qq"),
|
|
driver_id=plugin_driver.driver_id,
|
|
driver_kind=plugin_driver.descriptor.kind,
|
|
)
|
|
)
|
|
|
|
message = type("FakeMessage", (), {"message_id": "message-1"})()
|
|
batch = await manager.send_message(
|
|
message=message,
|
|
route_key=RouteKey(platform="qq"),
|
|
metadata={"show_log": False},
|
|
)
|
|
|
|
assert sorted(receipt.driver_id for receipt in batch.sent_receipts) == [
|
|
"legacy.send.qq",
|
|
"plugin.qq.sender",
|
|
]
|
|
assert batch.failed_receipts == []
|
|
assert len(legacy_calls) == 1
|
|
assert legacy_calls[0]["message"] is message
|
|
assert legacy_calls[0]["show_log"] is False
|
|
finally:
|
|
await manager.stop()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_legacy_platform_driver_uses_prepared_universal_sender(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""legacy driver 应复用已预处理消息的旧链发送函数。"""
|
|
calls: list[dict[str, Any]] = []
|
|
|
|
async def _fake_send_prepared_message_to_platform(message: Any, show_log: bool = True) -> bool:
|
|
"""记录 legacy driver 调用。"""
|
|
calls.append({"message": message, "show_log": show_log})
|
|
return True
|
|
|
|
monkeypatch.setattr(
|
|
uni_message_sender,
|
|
"send_prepared_message_to_platform",
|
|
_fake_send_prepared_message_to_platform,
|
|
)
|
|
|
|
driver = LegacyPlatformDriver(
|
|
driver_id="legacy.send.qq",
|
|
platform="qq",
|
|
account_id="bot-qq",
|
|
)
|
|
message = type("FakeMessage", (), {"message_id": "message-1"})()
|
|
receipt = await driver.send_message(
|
|
message=message,
|
|
route_key=RouteKey(platform="qq"),
|
|
metadata={"show_log": False},
|
|
)
|
|
|
|
assert len(calls) == 1
|
|
assert calls[0]["message"] is message
|
|
assert calls[0]["show_log"] is False
|
|
assert receipt.status == DeliveryStatus.SENT
|
|
assert receipt.driver_id == "legacy.send.qq"
|