mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-16 23:07:14 +00:00
108 lines
3.7 KiB
Python
108 lines
3.7 KiB
Python
"""Independent bot subscriptions must not change primary route delivery."""
|
|
|
|
import asyncio
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock
|
|
|
|
import pytest
|
|
from langbot_plugin.api.entities.builtin.platform.events import MemberJoinedEvent
|
|
|
|
from langbot.pkg.platform.botmgr import RuntimeBot
|
|
|
|
|
|
def make_bot(subscriptions):
|
|
bot = object.__new__(RuntimeBot)
|
|
bot.bot_entity = SimpleNamespace(uuid='bot', event_bindings=[], plugin_processors=subscriptions)
|
|
bot.execution_context = 'workspace'
|
|
bot._record_adapter_event = AsyncMock()
|
|
bot._record_event_route_trace = AsyncMock()
|
|
bot.logger = SimpleNamespace(error=AsyncMock())
|
|
return bot
|
|
|
|
|
|
async def test_slow_failed_and_duplicate_subscriptions_do_not_block_primary_route():
|
|
bot = make_bot(
|
|
[
|
|
{'processor_uuid': 'slow'},
|
|
{'processor_uuid': 'failed'},
|
|
{'processor_uuid': 'fast'},
|
|
{'processor_uuid': 'fast'},
|
|
{'processor_uuid': 'disabled', 'enabled': False},
|
|
]
|
|
)
|
|
started = []
|
|
release = asyncio.Event()
|
|
|
|
async def subscriber(event, adapter, uuid):
|
|
started.append(uuid)
|
|
if uuid == 'slow':
|
|
await release.wait()
|
|
if uuid == 'failed':
|
|
raise ValueError('plugin error')
|
|
|
|
async def primary(event, adapter):
|
|
started.append('primary')
|
|
|
|
bot._dispatch_plugin_subscription = subscriber
|
|
bot._dispatch_eba_event_to_processor = primary
|
|
task = asyncio.create_task(bot._handle_platform_event(MemberJoinedEvent(), None))
|
|
for _ in range(10):
|
|
await asyncio.sleep(0)
|
|
if len(started) == 4:
|
|
break
|
|
assert set(started) == {'primary', 'slow', 'failed', 'fast'}
|
|
assert started.count('fast') == 1
|
|
assert not task.done()
|
|
release.set()
|
|
await task
|
|
bot.logger.error.assert_awaited_once()
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'patterns,event_usage,expected',
|
|
[
|
|
(['group.member_joined'], True, 1),
|
|
(['group.*'], True, 1),
|
|
(['*'], True, 1),
|
|
(['message.received'], True, 0),
|
|
([], True, 0),
|
|
(['*'], False, 0),
|
|
],
|
|
)
|
|
async def test_matching_uses_live_runner_declaration(patterns, event_usage, expected):
|
|
bot = make_bot([])
|
|
bot.ap = SimpleNamespace(
|
|
agent_service=SimpleNamespace(
|
|
get_agent=AsyncMock(
|
|
return_value={
|
|
'kind': 'event_processor',
|
|
'component_ref': 'plugin:test/runner/default',
|
|
'supported_event_patterns': ['stale.event'],
|
|
}
|
|
)
|
|
),
|
|
runner_registry=SimpleNamespace(
|
|
get=AsyncMock(
|
|
return_value=SimpleNamespace(
|
|
usages=['event'] if event_usage else ['agent'],
|
|
supported_event_patterns=patterns,
|
|
)
|
|
)
|
|
),
|
|
)
|
|
bot._dispatch_eba_event_to_processor = AsyncMock()
|
|
await bot._dispatch_plugin_subscription(MemberJoinedEvent(), None, 'processor')
|
|
assert bot._dispatch_eba_event_to_processor.await_count == expected
|
|
if expected:
|
|
args = bot._dispatch_eba_event_to_processor.await_args.args
|
|
assert args[2]['target_uuid'] == 'processor'
|
|
assert args[3]['supported_event_patterns'] == patterns
|
|
|
|
|
|
async def test_missing_processor_is_logged_without_unscoped_lookup():
|
|
bot = make_bot([])
|
|
bot.ap = SimpleNamespace(agent_service=SimpleNamespace(get_agent=AsyncMock(return_value=None)))
|
|
await bot._dispatch_plugin_subscription(MemberJoinedEvent(), None, 'missing')
|
|
bot.ap.agent_service.get_agent.assert_awaited_once_with('workspace', 'missing')
|
|
assert bot._record_event_route_trace.await_args.kwargs['status'] == 'failed'
|