mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-16 14:57:15 +00:00
feat(bots): bind plugin processor configurations independently
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
"""Validate subscription identity, workspace boundaries and persisted updates."""
|
||||
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
|
||||
from langbot.pkg.api.http.service.bot import BotService
|
||||
from langbot.pkg.agent.runner.errors import RunnerNotFoundError
|
||||
|
||||
|
||||
def make_service(agent=None):
|
||||
service = BotService(
|
||||
SimpleNamespace(
|
||||
runner_registry=SimpleNamespace(
|
||||
get=AsyncMock(
|
||||
return_value=SimpleNamespace(
|
||||
usages=['event'],
|
||||
supported_event_patterns=['group.member_joined'],
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
service._get_agent_entity = AsyncMock(return_value=agent)
|
||||
return service
|
||||
|
||||
|
||||
async def test_prepare_accepts_configured_instance_and_preserves_input():
|
||||
service = make_service(SimpleNamespace(kind='event_processor', component_ref='plugin:test/runner/default'))
|
||||
payload = {'plugin_processors': [{'processor_uuid': 'processor', 'enabled': True, 'events': ['*']}]}
|
||||
result = await service._prepare_bot_data('workspace', payload, include_uuid=False)
|
||||
assert result == {'plugin_processors': [{'processor_uuid': 'processor', 'enabled': True}]}
|
||||
assert payload['plugin_processors'][0]['events'] == ['*']
|
||||
service._get_agent_entity.assert_awaited_once_with('workspace', 'processor')
|
||||
service.ap.runner_registry.get.assert_awaited_once_with('workspace', 'plugin:test/runner/default')
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'items,reason',
|
||||
[
|
||||
({}, 'must be an array'),
|
||||
([None], 'must be an object'),
|
||||
([{}], 'UUID is required'),
|
||||
([{'processor_uuid': 'p', 'enabled': 'false'}], 'must be a boolean'),
|
||||
([{'processor_uuid': 'p'}, {'processor_uuid': 'p'}], 'only be bound once'),
|
||||
],
|
||||
)
|
||||
async def test_invalid_bindings_are_rejected(items, reason):
|
||||
service = make_service(SimpleNamespace(kind='event_processor', component_ref='runner'))
|
||||
with pytest.raises(ValueError, match=reason):
|
||||
await service._normalize_plugin_processors('workspace', items)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('agent', [None, SimpleNamespace(kind='agent')])
|
||||
async def test_missing_cross_workspace_or_wrong_kind_is_rejected(agent):
|
||||
service = make_service(agent)
|
||||
with pytest.raises(ValueError, match='not found'):
|
||||
await service._normalize_plugin_processors('workspace', [{'processor_uuid': 'p'}])
|
||||
|
||||
|
||||
async def test_can_disable_binding_when_plugin_is_unavailable():
|
||||
service = make_service(SimpleNamespace(kind='event_processor', component_ref='missing'))
|
||||
service.ap.runner_registry.get.side_effect = ValueError('not installed')
|
||||
assert await service._normalize_plugin_processors('workspace', [{'processor_uuid': 'p', 'enabled': False}]) == [
|
||||
{'processor_uuid': 'p', 'enabled': False},
|
||||
]
|
||||
service.ap.runner_registry.get.assert_not_called()
|
||||
|
||||
|
||||
async def test_enabling_unavailable_runner_has_actionable_validation_error():
|
||||
service = make_service(SimpleNamespace(kind='event_processor', component_ref='missing'))
|
||||
service.ap.runner_registry.get.side_effect = RunnerNotFoundError('missing')
|
||||
with pytest.raises(ValueError, match='Runner component is unavailable'):
|
||||
await service._normalize_plugin_processors('workspace', [{'processor_uuid': 'p'}])
|
||||
Reference in New Issue
Block a user