mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-16 14:57:15 +00:00
144 lines
6.5 KiB
Python
144 lines
6.5 KiB
Python
"""Execute real Core boundaries with content canaries and early failures."""
|
|
|
|
import json
|
|
from types import SimpleNamespace
|
|
from unittest.mock import AsyncMock
|
|
from uuid import uuid4
|
|
|
|
import pytest
|
|
|
|
from langbot.pkg.telemetry import diagnostics as d
|
|
from langbot.pkg.api.http.context import ExecutionContext
|
|
from langbot.pkg.agent.runner.orchestrator import AgentRunOrchestrator
|
|
from langbot.pkg.agent.runner.reply_stream import ReplyStreamSession, ReplyStreamRequest
|
|
|
|
|
|
def make_ap():
|
|
ap = SimpleNamespace(instance_config=SimpleNamespace(data={'space': {'url': 'https://example.invalid'}}))
|
|
ap.persistence_mgr = SimpleNamespace(get_db_engine=lambda: None)
|
|
ap.diagnostics = d.DiagnosticsManager(ap, version='4.11.0b2', instance_id='instance-test')
|
|
return ap
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.parametrize('processor', ['pipeline', 'agent', 'event_processor'])
|
|
async def test_real_orchestrator_prepare_failure(processor):
|
|
ap = make_ap()
|
|
registry = SimpleNamespace(get=AsyncMock(side_effect=ValueError('CANARY private runner URL')))
|
|
orchestrator = AgentRunOrchestrator(ap, registry)
|
|
context = ExecutionContext(instance_uuid='instance-test', workspace_uuid=str(uuid4()), placement_generation=1)
|
|
event = SimpleNamespace(workspace_id=context.workspace_uuid, event_type='message.received')
|
|
binding = SimpleNamespace(runner_id='CANARY', processor_type=processor)
|
|
with pytest.raises(ValueError, match='CANARY'):
|
|
await anext(orchestrator.run(event, binding, adapter_context={'_execution_context': context}))
|
|
records = ap.diagnostics.pending
|
|
assert [r['outcome'] for r in records] == ['started', 'failed']
|
|
assert records[-1]['stage'] == 'prepare'
|
|
assert records[-1]['workspace_uuid'] == context.workspace_uuid
|
|
assert records[-1]['processor_type'] == processor
|
|
assert 'CANARY' not in json.dumps(records)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_real_reply_stream_mock_is_not_platform_success():
|
|
ap = make_ap()
|
|
event = SimpleNamespace(
|
|
delivery=SimpleNamespace(reply_target={}, surface='webui', platform_capabilities={'debug_mock': True})
|
|
)
|
|
session = ReplyStreamSession(event)
|
|
# Real runtime supplies the manager at construction from the orchestrator.
|
|
session.diagnostics = ap.diagnostics
|
|
result = await session.apply(ReplyStreamRequest(stream_id=uuid4(), operation='finish', text='CANARY user reply'))
|
|
assert result['mock'] is True and result['text'] == 'CANARY user reply'
|
|
assert ap.diagnostics.pending[-1]['source'] == 'webui_debug'
|
|
assert ap.diagnostics.pending[-1]['attributes']['synthetic'] is True
|
|
assert 'CANARY' not in json.dumps(ap.diagnostics.pending)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_real_bot_route_projects_status_not_reason():
|
|
from langbot.pkg.platform.botmgr import RuntimeBot
|
|
|
|
ap = make_ap()
|
|
bot = object.__new__(RuntimeBot)
|
|
bot.ap = ap
|
|
bot.logger = SimpleNamespace(info=AsyncMock())
|
|
await bot._record_event_route_trace(
|
|
event_type='message.received',
|
|
status='not_matched',
|
|
text='CANARY secret',
|
|
reason='CANARY',
|
|
failure_code='route_not_found',
|
|
)
|
|
records = ap.diagnostics.pending
|
|
assert records[-1]['outcome'] == 'skipped'
|
|
assert records[-1]['reason_code'] == 'route_not_found'
|
|
assert 'CANARY' not in json.dumps(records)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_real_telegram_conversion_failure_before_bot_manager(monkeypatch):
|
|
from langbot.pkg.platform.adapters.telegram.adapter import TelegramAdapter
|
|
from langbot.pkg.platform.adapters.telegram.event_converter import TelegramEventConverter
|
|
|
|
ap = make_ap()
|
|
context = ExecutionContext(instance_uuid='instance-test', workspace_uuid=str(uuid4()), placement_generation=1)
|
|
from langbot.pkg.platform.logger import EventLogger
|
|
|
|
logger = EventLogger('test', ap, context, 'test')
|
|
logger.error = AsyncMock()
|
|
logger.warning = AsyncMock()
|
|
adapter = TelegramAdapter({'token': '123456:ABCDEFGHIJKLMNOPQRSTUVWXYZ_123456789'}, logger)
|
|
adapter.listeners = {}
|
|
monkeypatch.setattr(
|
|
TelegramEventConverter, '_convert_message', AsyncMock(side_effect=ValueError('CANARY conversion'))
|
|
)
|
|
update = SimpleNamespace(
|
|
message=SimpleNamespace(from_user=SimpleNamespace(is_bot=False), text='CANARY text'),
|
|
edited_message=None,
|
|
chat_member=None,
|
|
my_chat_member=None,
|
|
callback_query=None,
|
|
message_reaction=None,
|
|
)
|
|
callback = adapter.application.handlers[0][0].callback
|
|
await callback(update, None)
|
|
records = [e for e in ap.diagnostics.pending if e['stage'] == 'convert' and e['outcome'] == 'failed']
|
|
assert records and records[-1]['adapter'] == 'telegram-omni'
|
|
assert records[-1]['workspace_uuid'] == context.workspace_uuid
|
|
assert 'CANARY' not in json.dumps(ap.diagnostics.pending)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_real_interaction_ack_skip_and_failure():
|
|
from langbot.pkg.agent.runner.interaction_manager import InteractionManager
|
|
|
|
ap = make_ap()
|
|
interactions = InteractionManager(ap, store=SimpleNamespace(record_delivery_success=AsyncMock()))
|
|
await interactions.acknowledge_submission({}, SimpleNamespace(get_supported_apis=lambda: []))
|
|
assert ap.diagnostics.pending[-1]['outcome'] == 'skipped'
|
|
ap.diagnostics.pending.clear()
|
|
ap.logger = SimpleNamespace(warning=lambda *a: None)
|
|
adapter = SimpleNamespace(
|
|
get_supported_apis=lambda: ['interaction.acknowledge'],
|
|
call_platform_api=AsyncMock(side_effect=ValueError('CANARY ack')),
|
|
)
|
|
await interactions.acknowledge_submission({'delivery_result': {'secret': 'CANARY'}}, adapter)
|
|
assert ap.diagnostics.pending[-1]['outcome'] == 'failed'
|
|
assert 'CANARY' not in json.dumps(ap.diagnostics.pending)
|
|
|
|
|
|
def test_actual_adapter_capability_snapshot():
|
|
from langbot.pkg.platform.adapters.telegram.adapter import TelegramAdapter
|
|
from langbot.pkg.telemetry.diagnostic_catalog import snapshot_bot
|
|
|
|
ap = make_ap()
|
|
context = ExecutionContext(instance_uuid='instance-test', workspace_uuid=str(uuid4()), placement_generation=1)
|
|
adapter = TelegramAdapter.model_construct(config={}, listeners={})
|
|
snapshot_bot(SimpleNamespace(ap=ap, adapter=adapter, execution_context=context))
|
|
records = ap.diagnostics.pending
|
|
assert records and all(e['kind'] == 'capability' for e in records)
|
|
api_rows = [e for e in records if e['attributes']['capability_type'] == 'api']
|
|
assert any(e['operation'] == 'send_message' and e['attributes']['supported'] for e in api_rows)
|
|
assert all(e['workspace_uuid'] == context.workspace_uuid for e in records)
|