use super::*;
#[test]
fn test_execution_context() {
let mut ctx = ExecutionContext::new();
ctx.set("key".to_string(), "value".to_string());
assert_eq!(ctx.get("key"), Some("value".to_string()));
}
#[test]
fn test_execution_status() {
assert_eq!(ExecutionStatus::Completed, ExecutionStatus::Completed);
assert_ne!(ExecutionStatus::Completed, ExecutionStatus::Failed);
}
#[test]
fn test_agent_telemetry_default() {
let telemetry = AgentTelemetry::default();
assert_eq!(telemetry.total_calls, 0);
assert_eq!(telemetry.total_tokens_in, 0);
assert_eq!(telemetry.total_tokens_out, 0);
assert_eq!(telemetry.total_latency_ms, 0);
assert_eq!(telemetry.avg_latency_ms, 0.0);
}
#[test]
fn test_workflow_telemetry_default() {
let telemetry = WorkflowTelemetry::default();
assert_eq!(telemetry.workflow_duration_ms, 0);
assert!(telemetry.agent_metrics.is_empty());
assert_eq!(telemetry.total_tokens, 0);
assert_eq!(telemetry.total_api_calls, 0);
}
#[tokio::test]
async fn test_telemetry_methods_with_dry_run() {
let runtime = SwlRuntime::new_dry_run();
let trace = runtime.get_execution_trace().await;
assert!(trace.is_empty());
let summary = runtime.get_telemetry_summary().await;
assert_eq!(summary.total_api_calls, 0);
assert!(summary.agent_metrics.is_empty());
let agent_trace = runtime.get_agent_trace("test_agent").await;
assert!(agent_trace.is_empty());
}
#[tokio::test]
async fn execute_tool_call_blocks_dangerous_shell_command() {
let runtime = SwlRuntime::new_dry_run();
let call = crate::api::types::ToolCall {
id: "t1".to_string(),
call_type: "function".to_string(),
function: crate::api::types::ToolFunction {
name: "shell_exec".to_string(),
arguments: r#"{"command":"rm -rf /"}"#.to_string(),
},
};
let available = vec!["shell_exec".to_string()];
let err = runtime
.execute_tool_call(&call, &available)
.await
.expect_err("dangerous shell command must be blocked");
assert!(err.to_string().contains("Dangerous command"));
}
#[tokio::test]
async fn execute_tool_call_allows_safe_shell_command() {
let runtime = SwlRuntime::new_dry_run();
let call = crate::api::types::ToolCall {
id: "t2".to_string(),
call_type: "function".to_string(),
function: crate::api::types::ToolFunction {
name: "shell_exec".to_string(),
arguments: r#"{"command":"echo hello"}"#.to_string(),
},
};
let available = vec!["shell_exec".to_string()];
let result = runtime.execute_tool_call(&call, &available).await;
assert!(result.is_ok(), "expected safe command to run: {result:?}");
}
#[tokio::test]
async fn test_clear_telemetry() {
let runtime = SwlRuntime::new_dry_run();
{
let mut ctx = runtime.context.lock().await;
ctx.trace.push(ExecutionEvent {
timestamp: std::time::Instant::now(),
agent: "test".to_string(),
action: "test".to_string(),
duration_ms: 100,
tokens_in: 10,
tokens_out: 20,
});
}
let trace = runtime.get_execution_trace().await;
assert_eq!(trace.len(), 1);
runtime.clear_telemetry().await;
let trace = runtime.get_execution_trace().await;
assert!(trace.is_empty());
}
#[tokio::test]
async fn test_telemetry_summary_aggregation() {
let runtime = SwlRuntime::new_dry_run();
{
let mut ctx = runtime.context.lock().await;
ctx.trace.push(ExecutionEvent {
timestamp: std::time::Instant::now(),
agent: "agent1".to_string(),
action: "llm_call".to_string(),
duration_ms: 100,
tokens_in: 10,
tokens_out: 20,
});
ctx.trace.push(ExecutionEvent {
timestamp: std::time::Instant::now(),
agent: "agent1".to_string(),
action: "llm_call".to_string(),
duration_ms: 200,
tokens_in: 20,
tokens_out: 30,
});
ctx.trace.push(ExecutionEvent {
timestamp: std::time::Instant::now(),
agent: "agent2".to_string(),
action: "llm_call".to_string(),
duration_ms: 150,
tokens_in: 15,
tokens_out: 25,
});
}
let summary = runtime.get_telemetry_summary().await;
assert_eq!(summary.total_api_calls, 3);
assert_eq!(summary.total_tokens, 120);
let agent1 = summary
.agent_metrics
.get("agent1")
.expect("agent1 should exist");
assert_eq!(agent1.total_calls, 2);
assert_eq!(agent1.total_tokens_in, 30);
assert_eq!(agent1.total_tokens_out, 50);
assert_eq!(agent1.total_latency_ms, 300);
assert_eq!(agent1.avg_latency_ms, 150.0);
let agent2 = summary
.agent_metrics
.get("agent2")
.expect("agent2 should exist");
assert_eq!(agent2.total_calls, 1);
assert_eq!(agent2.total_tokens_in, 15);
assert_eq!(agent2.total_tokens_out, 25);
assert_eq!(agent2.total_latency_ms, 150);
assert_eq!(agent2.avg_latency_ms, 150.0);
}
#[tokio::test]
async fn test_get_agent_trace() {
let runtime = SwlRuntime::new_dry_run();
{
let mut ctx = runtime.context.lock().await;
ctx.trace.push(ExecutionEvent {
timestamp: std::time::Instant::now(),
agent: "agent1".to_string(),
action: "llm_call".to_string(),
duration_ms: 100,
tokens_in: 10,
tokens_out: 20,
});
ctx.trace.push(ExecutionEvent {
timestamp: std::time::Instant::now(),
agent: "agent2".to_string(),
action: "llm_call".to_string(),
duration_ms: 200,
tokens_in: 20,
tokens_out: 30,
});
ctx.trace.push(ExecutionEvent {
timestamp: std::time::Instant::now(),
agent: "agent1".to_string(),
action: "llm_call".to_string(),
duration_ms: 150,
tokens_in: 15,
tokens_out: 25,
});
}
let agent1_trace = runtime.get_agent_trace("agent1").await;
assert_eq!(agent1_trace.len(), 2);
assert_eq!(agent1_trace[0].duration_ms, 100);
assert_eq!(agent1_trace[1].duration_ms, 150);
let agent2_trace = runtime.get_agent_trace("agent2").await;
assert_eq!(agent2_trace.len(), 1);
assert_eq!(agent2_trace[0].duration_ms, 200);
}
#[tokio::test]
async fn test_export_telemetry_json() {
let runtime = SwlRuntime::new_dry_run();
{
let mut ctx = runtime.context.lock().await;
ctx.trace.push(ExecutionEvent {
timestamp: std::time::Instant::now(),
agent: "test_agent".to_string(),
action: "llm_call".to_string(),
duration_ms: 100,
tokens_in: 10,
tokens_out: 20,
});
}
let json = runtime.export_telemetry_json().await;
assert!(json.is_ok());
let json_str = json.unwrap();
assert!(json_str.contains("test_agent"));
assert!(json_str.contains("total_tokens"));
assert!(json_str.contains("total_api_calls"));
}
#[test]
fn test_execution_event_creation() {
let event = ExecutionEvent {
timestamp: std::time::Instant::now(),
agent: "test_agent".to_string(),
action: "llm_call".to_string(),
duration_ms: 150,
tokens_in: 50,
tokens_out: 100,
};
assert_eq!(event.agent, "test_agent");
assert_eq!(event.action, "llm_call");
assert_eq!(event.duration_ms, 150);
assert_eq!(event.tokens_in, 50);
assert_eq!(event.tokens_out, 100);
}
#[test]
fn test_get_agent_tools() {
let runtime = SwlRuntime::new_dry_run();
let agent = AgentDefinition {
model: crate::swl::parser::ast::ModelSpec::Simple("test-model".to_string()),
role: None,
instruction: None,
tools: vec!["file_read".to_string(), "file_write".to_string()],
output_key: None,
sub_agents: vec![],
};
let tools = runtime.get_agent_tools(&agent);
assert_eq!(tools.len(), 2);
assert!(tools.contains(&"file_read".to_string()));
assert!(tools.contains(&"file_write".to_string()));
}
#[test]
fn test_get_agent_tools_empty() {
let runtime = SwlRuntime::new_dry_run();
let agent = AgentDefinition {
model: crate::swl::parser::ast::ModelSpec::Simple("test-model".to_string()),
role: None,
instruction: None,
tools: vec![],
output_key: None,
sub_agents: vec![],
};
let tools = runtime.get_agent_tools(&agent);
assert!(tools.is_empty());
}
#[test]
fn test_build_system_prompt_with_tools() {
let runtime = SwlRuntime::new_dry_run();
let agent = AgentDefinition {
model: crate::swl::parser::ast::ModelSpec::Simple("test-model".to_string()),
role: Some("You are a coding assistant.".to_string()),
instruction: None,
tools: vec!["file_read".to_string()],
output_key: None,
sub_agents: vec![],
};
let tools = vec!["file_read".to_string()];
let prompt = runtime.build_system_prompt(&agent, &tools);
assert!(prompt.contains("You are a coding assistant."));
assert!(prompt.contains("file_read"));
assert!(prompt.contains("<tool>"));
assert!(prompt.contains("</tool>"));
}
#[test]
fn test_build_system_prompt_no_tools() {
let runtime = SwlRuntime::new_dry_run();
let agent = AgentDefinition {
model: crate::swl::parser::ast::ModelSpec::Simple("test-model".to_string()),
role: Some("You are a helpful assistant.".to_string()),
instruction: None,
tools: vec![],
output_key: None,
sub_agents: vec![],
};
let tools: Vec<String> = vec![];
let prompt = runtime.build_system_prompt(&agent, &tools);
assert_eq!(prompt, "You are a helpful assistant.");
}
#[test]
fn test_build_tool_definitions() {
let runtime = SwlRuntime::new_dry_run();
let tool_names = vec!["file_read".to_string(), "shell_exec".to_string()];
let definitions = runtime.build_tool_definitions(&tool_names);
assert_eq!(definitions.len(), 2);
let names: Vec<&str> = definitions
.iter()
.map(|d| d.function.name.as_str())
.collect();
assert!(names.contains(&"file_read"));
assert!(names.contains(&"shell_exec"));
}
#[test]
fn test_build_tool_definitions_empty() {
let runtime = SwlRuntime::new_dry_run();
let tool_names: Vec<String> = vec![];
let definitions = runtime.build_tool_definitions(&tool_names);
assert!(definitions.is_empty());
}
#[test]
fn test_max_tool_iterations_config() {
let runtime = SwlRuntime::new_dry_run().with_max_tool_iterations(100);
assert_eq!(runtime.max_tool_iterations, 100);
}