mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-25 14:53:34 +00:00
test: add event-trigger routine e2e coverage (#1088)
This commit is contained in:
@@ -9,6 +9,10 @@ mod support;
|
||||
mod advanced {
|
||||
use std::time::Duration;
|
||||
|
||||
use ironclaw::agent::routine::Trigger;
|
||||
use ironclaw::channels::IncomingMessage;
|
||||
use ironclaw::db::Database;
|
||||
|
||||
use crate::support::cleanup::CleanupGuard;
|
||||
use crate::support::test_rig::TestRigBuilder;
|
||||
use crate::support::trace_llm::LlmTrace;
|
||||
@@ -19,6 +23,28 @@ mod advanced {
|
||||
);
|
||||
const TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
async fn wait_for_routine_run(
|
||||
db: &std::sync::Arc<dyn Database>,
|
||||
routine_id: uuid::Uuid,
|
||||
timeout: Duration,
|
||||
) -> Vec<ironclaw::agent::routine::RoutineRun> {
|
||||
let deadline = tokio::time::Instant::now() + timeout;
|
||||
loop {
|
||||
let runs = db
|
||||
.list_routine_runs(routine_id, 10)
|
||||
.await
|
||||
.expect("list_routine_runs");
|
||||
if !runs.is_empty() {
|
||||
return runs;
|
||||
}
|
||||
assert!(
|
||||
tokio::time::Instant::now() < deadline,
|
||||
"timed out waiting for routine run"
|
||||
);
|
||||
tokio::time::sleep(Duration::from_millis(100)).await;
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 1. Multi-turn memory coherence
|
||||
// -----------------------------------------------------------------------
|
||||
@@ -380,6 +406,118 @@ mod advanced {
|
||||
rig.shutdown();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 6b. Event routine: Telegram-scoped trigger fires on matching message
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[tokio::test]
|
||||
async fn routine_event_trigger_telegram_channel_fires() {
|
||||
let trace = LlmTrace::from_file(format!("{FIXTURES}/routine_event_telegram.json")).unwrap();
|
||||
let rig = TestRigBuilder::new()
|
||||
.with_trace(trace.clone())
|
||||
.with_routines()
|
||||
.with_auto_approve_tools(true)
|
||||
.build()
|
||||
.await;
|
||||
|
||||
rig.send_message(
|
||||
"Create a routine that watches Telegram messages starting with 'bug:' and alerts me.",
|
||||
)
|
||||
.await;
|
||||
let create_responses = rig.wait_for_responses(1, TIMEOUT).await;
|
||||
rig.verify_trace_expects(&trace, &create_responses);
|
||||
|
||||
let routine = rig
|
||||
.database()
|
||||
.get_routine_by_name("test-user", "telegram-bug-watcher")
|
||||
.await
|
||||
.expect("get_routine_by_name")
|
||||
.expect("telegram-bug-watcher should exist");
|
||||
|
||||
match &routine.trigger {
|
||||
Trigger::Event { channel, pattern } => {
|
||||
assert_eq!(channel.as_deref(), Some("telegram"));
|
||||
assert_eq!(pattern, "^bug\\b");
|
||||
}
|
||||
other => panic!("expected event trigger, got {other:?}"),
|
||||
}
|
||||
|
||||
rig.send_incoming(IncomingMessage::new(
|
||||
"telegram",
|
||||
"test-user",
|
||||
"bug: home button broken",
|
||||
))
|
||||
.await;
|
||||
|
||||
let runs = wait_for_routine_run(rig.database(), routine.id, TIMEOUT).await;
|
||||
assert_eq!(runs[0].trigger_type, "event");
|
||||
|
||||
let responses = rig.wait_for_responses(3, TIMEOUT).await;
|
||||
assert!(
|
||||
responses.iter().any(|response| {
|
||||
response
|
||||
.metadata
|
||||
.get("source")
|
||||
.and_then(|value| value.as_str())
|
||||
== Some("routine")
|
||||
&& response.content.contains("telegram-bug-watcher")
|
||||
&& response.content.contains("Bug report detected")
|
||||
}),
|
||||
"expected routine notification in responses: {responses:?}"
|
||||
);
|
||||
|
||||
rig.shutdown();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 6c. Event routine without channel filter still fires on Telegram
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
#[tokio::test]
|
||||
async fn routine_event_trigger_without_channel_filter_still_fires() {
|
||||
let trace =
|
||||
LlmTrace::from_file(format!("{FIXTURES}/routine_event_any_channel.json")).unwrap();
|
||||
let rig = TestRigBuilder::new()
|
||||
.with_trace(trace)
|
||||
.with_routines()
|
||||
.with_auto_approve_tools(true)
|
||||
.build()
|
||||
.await;
|
||||
|
||||
rig.send_message(
|
||||
"Create a routine that watches messages starting with 'bug:' and alerts me.",
|
||||
)
|
||||
.await;
|
||||
let _ = rig.wait_for_responses(1, TIMEOUT).await;
|
||||
|
||||
let routine = rig
|
||||
.database()
|
||||
.get_routine_by_name("test-user", "any-channel-bug-watcher")
|
||||
.await
|
||||
.expect("get_routine_by_name")
|
||||
.expect("any-channel-bug-watcher should exist");
|
||||
|
||||
match &routine.trigger {
|
||||
Trigger::Event { channel, pattern } => {
|
||||
assert_eq!(channel, &None);
|
||||
assert_eq!(pattern, "^bug\\b");
|
||||
}
|
||||
other => panic!("expected event trigger, got {other:?}"),
|
||||
}
|
||||
|
||||
rig.send_incoming(IncomingMessage::new(
|
||||
"telegram",
|
||||
"test-user",
|
||||
"bug: login button broken",
|
||||
))
|
||||
.await;
|
||||
|
||||
let runs = wait_for_routine_run(rig.database(), routine.id, TIMEOUT).await;
|
||||
assert_eq!(runs[0].trigger_type, "event");
|
||||
|
||||
rig.shutdown();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// 7. Prompt injection resilience
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"model_name": "advanced-routine-event-any-channel",
|
||||
"expects": {
|
||||
"tools_used": ["routine_create"],
|
||||
"all_tools_succeeded": true,
|
||||
"min_responses": 1
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"response": {
|
||||
"type": "tool_calls",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_routine_create_event_any_channel",
|
||||
"name": "routine_create",
|
||||
"arguments": {
|
||||
"name": "any-channel-bug-watcher",
|
||||
"description": "Watch bug reports from any incoming channel.",
|
||||
"trigger_type": "event",
|
||||
"event_pattern": "^bug\\b",
|
||||
"prompt": "Summarize the bug report in one line."
|
||||
}
|
||||
}
|
||||
],
|
||||
"input_tokens": 130,
|
||||
"output_tokens": 38
|
||||
}
|
||||
},
|
||||
{
|
||||
"response": {
|
||||
"type": "text",
|
||||
"content": "Created the any-channel-bug-watcher routine for bug messages.",
|
||||
"input_tokens": 170,
|
||||
"output_tokens": 18
|
||||
}
|
||||
},
|
||||
{
|
||||
"response": {
|
||||
"type": "text",
|
||||
"content": "I saw the Telegram message.",
|
||||
"input_tokens": 90,
|
||||
"output_tokens": 12
|
||||
}
|
||||
},
|
||||
{
|
||||
"response": {
|
||||
"type": "text",
|
||||
"content": "Bug report detected: login button broken.",
|
||||
"input_tokens": 120,
|
||||
"output_tokens": 14
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
{
|
||||
"model_name": "advanced-routine-event-telegram",
|
||||
"expects": {
|
||||
"tools_used": ["routine_create"],
|
||||
"all_tools_succeeded": true,
|
||||
"min_responses": 1
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"response": {
|
||||
"type": "tool_calls",
|
||||
"tool_calls": [
|
||||
{
|
||||
"id": "call_routine_create_event_telegram",
|
||||
"name": "routine_create",
|
||||
"arguments": {
|
||||
"name": "telegram-bug-watcher",
|
||||
"description": "Watch Telegram bug reports and alert on them.",
|
||||
"trigger_type": "event",
|
||||
"event_channel": "telegram",
|
||||
"event_pattern": "^bug\\b",
|
||||
"prompt": "Summarize the bug report in one line."
|
||||
}
|
||||
}
|
||||
],
|
||||
"input_tokens": 140,
|
||||
"output_tokens": 40
|
||||
}
|
||||
},
|
||||
{
|
||||
"response": {
|
||||
"type": "text",
|
||||
"content": "Created the telegram-bug-watcher routine for Telegram bug messages.",
|
||||
"input_tokens": 180,
|
||||
"output_tokens": 20
|
||||
}
|
||||
},
|
||||
{
|
||||
"response": {
|
||||
"type": "text",
|
||||
"content": "I saw the Telegram message.",
|
||||
"input_tokens": 90,
|
||||
"output_tokens": 12
|
||||
}
|
||||
},
|
||||
{
|
||||
"response": {
|
||||
"type": "text",
|
||||
"content": "Bug report detected: home button broken.",
|
||||
"input_tokens": 120,
|
||||
"output_tokens": 14
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user