mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-09-01 15:17:15 +00:00
fix(bots): explain unavailable route tests
This commit is contained in:
@@ -22,6 +22,7 @@ class BotService:
|
|||||||
FAILURE_PROCESSOR_NOT_FOUND = 'processor_not_found'
|
FAILURE_PROCESSOR_NOT_FOUND = 'processor_not_found'
|
||||||
FAILURE_PROCESSOR_INCOMPATIBLE = 'processor_incompatible'
|
FAILURE_PROCESSOR_INCOMPATIBLE = 'processor_incompatible'
|
||||||
FAILURE_INVALID_EVENT = 'invalid_event'
|
FAILURE_INVALID_EVENT = 'invalid_event'
|
||||||
|
FAILURE_BOT_RUNTIME_UNAVAILABLE = 'bot_runtime_unavailable'
|
||||||
ROUTE_TRACE_KIND = 'event_route_trace'
|
ROUTE_TRACE_KIND = 'event_route_trace'
|
||||||
|
|
||||||
BOT_FIELDS = {
|
BOT_FIELDS = {
|
||||||
@@ -846,7 +847,14 @@ class BotService:
|
|||||||
raise WorkspaceNotFoundError('Bot not found')
|
raise WorkspaceNotFoundError('Bot not found')
|
||||||
runtime_bot = await self.ap.platform_mgr.get_bot_by_uuid(context, bot_uuid)
|
runtime_bot = await self.ap.platform_mgr.get_bot_by_uuid(context, bot_uuid)
|
||||||
if runtime_bot is None:
|
if runtime_bot is None:
|
||||||
raise Exception('Bot not found')
|
return {
|
||||||
|
'dispatched': False,
|
||||||
|
'event_type': event_type,
|
||||||
|
'failure_code': self.FAILURE_BOT_RUNTIME_UNAVAILABLE,
|
||||||
|
'reason': 'Bot runtime is unavailable',
|
||||||
|
'suppressed_outputs': [],
|
||||||
|
'route_status': await self.list_event_route_statuses(context, bot_uuid),
|
||||||
|
}
|
||||||
|
|
||||||
dispatch_result = await runtime_bot.dispatch_test_event(event_type, payload or {})
|
dispatch_result = await runtime_bot.dispatch_test_event(event_type, payload or {})
|
||||||
route_status = await self.list_event_route_statuses(context, bot_uuid)
|
route_status = await self.list_event_route_statuses(context, bot_uuid)
|
||||||
|
|||||||
@@ -631,6 +631,46 @@ class TestBotServiceListEventRouteStatuses:
|
|||||||
assert result['stale_routes'] == []
|
assert result['stale_routes'] == []
|
||||||
|
|
||||||
|
|
||||||
|
class TestBotServiceDispatchTestEventRoute:
|
||||||
|
"""Tests for dispatching a synthetic event through a saved route."""
|
||||||
|
|
||||||
|
async def test_returns_actionable_failure_when_runtime_bot_is_unavailable(self):
|
||||||
|
ap = SimpleNamespace()
|
||||||
|
ap.platform_mgr = SimpleNamespace()
|
||||||
|
ap.platform_mgr.get_bot_by_uuid = AsyncMock(return_value=None)
|
||||||
|
|
||||||
|
service = BotService(ap)
|
||||||
|
service.get_bot = AsyncMock(return_value={'uuid': 'bot-uuid'})
|
||||||
|
service.list_event_route_statuses = AsyncMock(
|
||||||
|
return_value={
|
||||||
|
'routes': [],
|
||||||
|
'unmatched_events': [],
|
||||||
|
'stale_routes': [],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
result = await service.dispatch_test_event_route(
|
||||||
|
WORKSPACE_UUID,
|
||||||
|
'bot-uuid',
|
||||||
|
'message.received',
|
||||||
|
{'message_text': 'Hello'},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert result == {
|
||||||
|
'dispatched': False,
|
||||||
|
'event_type': 'message.received',
|
||||||
|
'failure_code': 'bot_runtime_unavailable',
|
||||||
|
'reason': 'Bot runtime is unavailable',
|
||||||
|
'suppressed_outputs': [],
|
||||||
|
'route_status': {
|
||||||
|
'routes': [],
|
||||||
|
'unmatched_events': [],
|
||||||
|
'stale_routes': [],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
service.list_event_route_statuses.assert_awaited_once_with(WORKSPACE_UUID, 'bot-uuid')
|
||||||
|
|
||||||
|
|
||||||
class TestBotServiceSendMessage:
|
class TestBotServiceSendMessage:
|
||||||
"""Tests for send_message method."""
|
"""Tests for send_message method."""
|
||||||
|
|
||||||
|
|||||||
@@ -964,6 +964,7 @@ function RouteDryRunDialog({
|
|||||||
|
|
||||||
async function dispatchTestEvent() {
|
async function dispatchTestEvent() {
|
||||||
setRunError(null);
|
setRunError(null);
|
||||||
|
setResult(null);
|
||||||
setDispatchResult(null);
|
setDispatchResult(null);
|
||||||
|
|
||||||
const payload = parsePayload();
|
const payload = parsePayload();
|
||||||
|
|||||||
@@ -466,10 +466,12 @@ const enUS = {
|
|||||||
'The selected processor cannot handle this event.',
|
'The selected processor cannot handle this event.',
|
||||||
processor_not_found: 'The selected processor is unavailable.',
|
processor_not_found: 'The selected processor is unavailable.',
|
||||||
processor_disabled: 'The selected processor is disabled.',
|
processor_disabled: 'The selected processor is disabled.',
|
||||||
|
bot_runtime_unavailable:
|
||||||
|
'The bot is not running. Check its platform settings and enable it before running a full test.',
|
||||||
runner_failed: 'The Agent runner failed while processing the event.',
|
runner_failed: 'The Agent runner failed while processing the event.',
|
||||||
delivery_failed: 'The processor finished, but delivery failed.',
|
delivery_failed: 'The processor finished, but delivery failed.',
|
||||||
},
|
},
|
||||||
routeTestAction: 'Run saved configuration',
|
routeTestAction: 'Run full test',
|
||||||
routeTestRunning: 'Running…',
|
routeTestRunning: 'Running…',
|
||||||
routeTestFailed: 'Failed to run the saved route. Try again later.',
|
routeTestFailed: 'Failed to run the saved route. Try again later.',
|
||||||
routeTestDispatched:
|
routeTestDispatched:
|
||||||
|
|||||||
@@ -473,10 +473,12 @@ const jaJP = {
|
|||||||
'選択したプロセッサーはこのイベントを処理できません。',
|
'選択したプロセッサーはこのイベントを処理できません。',
|
||||||
processor_not_found: '選択したプロセッサーを利用できません。',
|
processor_not_found: '選択したプロセッサーを利用できません。',
|
||||||
processor_disabled: '選択したプロセッサーは無効です。',
|
processor_disabled: '選択したプロセッサーは無効です。',
|
||||||
|
bot_runtime_unavailable:
|
||||||
|
'ボットが実行されていません。プラットフォーム設定を確認してボットを有効にした後、完全テストを実行してください。',
|
||||||
runner_failed: 'Agent Runner がイベント処理中に失敗しました。',
|
runner_failed: 'Agent Runner がイベント処理中に失敗しました。',
|
||||||
delivery_failed: '処理は完了しましたが、結果の配信に失敗しました。',
|
delivery_failed: '処理は完了しましたが、結果の配信に失敗しました。',
|
||||||
},
|
},
|
||||||
routeTestAction: '保存済み設定を実行',
|
routeTestAction: '完全テストを実行',
|
||||||
routeTestRunning: '実行中…',
|
routeTestRunning: '実行中…',
|
||||||
routeTestFailed:
|
routeTestFailed:
|
||||||
'保存済みルートの実行に失敗しました。後でもう一度お試しください。',
|
'保存済みルートの実行に失敗しました。後でもう一度お試しください。',
|
||||||
|
|||||||
@@ -444,10 +444,12 @@ const zhHans = {
|
|||||||
processor_incompatible: '所选处理器无法处理此事件。',
|
processor_incompatible: '所选处理器无法处理此事件。',
|
||||||
processor_not_found: '所选处理器不可用。',
|
processor_not_found: '所选处理器不可用。',
|
||||||
processor_disabled: '所选处理器已禁用。',
|
processor_disabled: '所选处理器已禁用。',
|
||||||
|
bot_runtime_unavailable:
|
||||||
|
'机器人尚未运行。请检查平台配置并启用机器人,再运行完整测试。',
|
||||||
runner_failed: 'Agent Runner 处理事件时失败。',
|
runner_failed: 'Agent Runner 处理事件时失败。',
|
||||||
delivery_failed: '处理器已完成,但结果投递失败。',
|
delivery_failed: '处理器已完成,但结果投递失败。',
|
||||||
},
|
},
|
||||||
routeTestAction: '运行已保存配置',
|
routeTestAction: '运行完整测试',
|
||||||
routeTestRunning: '运行中…',
|
routeTestRunning: '运行中…',
|
||||||
routeTestFailed: '运行已保存路由失败,请稍后重试。',
|
routeTestFailed: '运行已保存路由失败,请稍后重试。',
|
||||||
routeTestDispatched: '已保存路由运行成功,{{count}} 个平台操作已被阻止。',
|
routeTestDispatched: '已保存路由运行成功,{{count}} 个平台操作已被阻止。',
|
||||||
|
|||||||
@@ -346,6 +346,53 @@ test.describe('bot advanced flows', () => {
|
|||||||
body: JSON.stringify({ code: -1, msg: 'Internal server error' }),
|
body: JSON.stringify({ code: -1, msg: 'Internal server error' }),
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
await page.route(
|
||||||
|
'**/api/v1/platform/bots/*/event-routes/dry-run',
|
||||||
|
(route) =>
|
||||||
|
route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
code: 0,
|
||||||
|
msg: 'ok',
|
||||||
|
data: {
|
||||||
|
matched: true,
|
||||||
|
event_type: 'message.received',
|
||||||
|
matched_binding_id: 'binding-1',
|
||||||
|
matched_binding_index: 0,
|
||||||
|
target: {
|
||||||
|
target_type: 'agent',
|
||||||
|
target_uuid: 'agent-1',
|
||||||
|
target_name: 'NewAgent',
|
||||||
|
},
|
||||||
|
diagnostic_steps: ['Matched route 1'],
|
||||||
|
diagnostic_details: [],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
await page.route('**/api/v1/platform/bots/*/event-routes/test', (route) =>
|
||||||
|
route.fulfill({
|
||||||
|
status: 200,
|
||||||
|
contentType: 'application/json',
|
||||||
|
body: JSON.stringify({
|
||||||
|
code: 0,
|
||||||
|
msg: 'ok',
|
||||||
|
data: {
|
||||||
|
dispatched: false,
|
||||||
|
event_type: 'message.received',
|
||||||
|
failure_code: 'bot_runtime_unavailable',
|
||||||
|
reason: 'Bot runtime is unavailable',
|
||||||
|
suppressed_outputs: [],
|
||||||
|
route_status: {
|
||||||
|
routes: [],
|
||||||
|
unmatched_events: [],
|
||||||
|
stale_routes: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
await page.goto('/home/bots?id=new');
|
await page.goto('/home/bots?id=new');
|
||||||
await selectPlaywrightAdapter(page);
|
await selectPlaywrightAdapter(page);
|
||||||
@@ -397,8 +444,28 @@ test.describe('bot advanced flows', () => {
|
|||||||
routeDialog.getByRole('button', { name: 'Preview match' }),
|
routeDialog.getByRole('button', { name: 'Preview match' }),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await expect(
|
await expect(
|
||||||
routeDialog.getByRole('button', { name: 'Run saved configuration' }),
|
routeDialog.getByRole('button', { name: 'Run full test' }),
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
|
await routeDialog.getByRole('button', { name: 'Preview match' }).click();
|
||||||
|
await expect(routeDialog.getByText('Matched route')).toBeVisible();
|
||||||
|
|
||||||
|
await routeDialog.getByRole('button', { name: 'Run full test' }).click();
|
||||||
|
await expect(routeDialog.getByText('Matched route')).toHaveCount(0);
|
||||||
|
await expect(
|
||||||
|
routeDialog.getByText(
|
||||||
|
'The bot is not running. Check its platform settings and enable it before running a full test.',
|
||||||
|
),
|
||||||
|
).toBeVisible();
|
||||||
|
await expect(routeDialog.getByText('Internal server error')).toHaveCount(0);
|
||||||
|
|
||||||
|
await routeDialog.getByRole('button', { name: 'Preview match' }).click();
|
||||||
|
await expect(
|
||||||
|
routeDialog.getByText(
|
||||||
|
'The bot is not running. Check its platform settings and enable it before running a full test.',
|
||||||
|
),
|
||||||
|
).toHaveCount(0);
|
||||||
|
await expect(routeDialog.getByText('Matched route')).toBeVisible();
|
||||||
const dialogBox = await routeDialog.boundingBox();
|
const dialogBox = await routeDialog.boundingBox();
|
||||||
expect(dialogBox).not.toBeNull();
|
expect(dialogBox).not.toBeNull();
|
||||||
expect(dialogBox!.height).toBeLessThan(500);
|
expect(dialogBox!.height).toBeLessThan(500);
|
||||||
|
|||||||
Reference in New Issue
Block a user