type Result<T> = core::result::Result<T, Box<dyn std::error::Error>>;
use super::*;
use crate::_test_support::{assert_contains, load_inline_agent, load_test_agent, run_test_agent_with_input};
use crate::types::FileMeta;
use simple_fs::SPath;
#[tokio::test]
async fn test_run_agent_script_hello_ok() -> Result<()> {
let runtime = Runtime::new_test_runtime_sandbox_01()?;
let agent = load_test_agent("./agent-script/agent-hello.devai", &runtime)?;
let res = run_test_agent_with_input(&runtime, &agent, "input-01").await?;
assert_eq!(
res.as_str().ok_or("Should have output result")?,
"Hello 'input-01' from agent-hello.devai"
);
Ok(())
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_run_agent_script_before_all_simple() -> Result<()> {
let runtime = Runtime::new_test_runtime_sandbox_01()?;
let agent = load_test_agent("./agent-script/agent-before-all.devai", &runtime)?;
let on_path = SPath::new("./some-random/file.txt")?;
let path_ref = FileMeta::from(on_path);
let inputs = vec![serde_json::to_value(path_ref)?];
let res = run_command_agent(&runtime, agent, Some(inputs), &RunBaseOptions::default(), true).await?;
let outputs = res.outputs.ok_or("Should have output values")?;
assert_eq!(outputs.len(), 1, "should have one and only one output");
let output = outputs.into_iter().next().ok_or("Should have one output")?;
assert_eq!(
output.as_str().ok_or("Output should be string")?,
"Some Before All - Some Data - ./some-random/file.txt"
);
Ok(())
}
#[tokio::test]
async fn test_run_agent_script_with_options_read() -> Result<()> {
let runtime = Runtime::new_test_runtime_sandbox_01()?;
let agent = load_test_agent("./agent-script/agent-options.devai", &runtime)?;
let inputs = vec!["one".into(), "two".into()];
let res = run_command_agent(&runtime, agent, Some(inputs), &RunBaseOptions::default(), true).await?;
let outputs = res.outputs.ok_or("Should have output values")?;
let first_output = outputs
.first()
.ok_or("Should have at least one output")?
.as_str()
.ok_or("after_all should be str")?;
let after_all = res.after_all.ok_or("should have after_all")?;
let after_all = after_all.as_str().ok_or("after_all should be str")?;
assert_contains(first_output, "b_r_model: deepseek-chat");
assert_contains(first_output, "i_model: cost-saver");
assert_contains(first_output, "i_r_model: deepseek-chat");
assert_contains(first_output, "o_model: cost-saver");
assert_contains(first_output, "o_r_model: deepseek-chat");
assert_contains(after_all, "a_r_model: deepseek-chat");
assert_contains(after_all, "a_b_r_model: deepseek-chat");
Ok(())
}
#[tokio::test]
async fn test_run_agent_script_before_all_inputs_reshape() -> Result<()> {
let runtime = Runtime::new_test_runtime_sandbox_01()?;
let agent = load_test_agent("./agent-script/agent-before-all-inputs-reshape.devai", &runtime)?;
let inputs = vec!["one".into(), "two".into()];
let res = run_command_agent(&runtime, agent, Some(inputs), &RunBaseOptions::default(), true)
.await?
.outputs
.ok_or("Should have output values")?;
let res = res.iter().map(|v| v.as_str().unwrap_or_default()).collect::<Vec<_>>();
assert_eq!(res[0], "Data with input: 'one-0'");
assert_eq!(res[1], "Data with input: 'two-1'");
assert_eq!(res[2], "Data with input: 'C'");
Ok(())
}
#[tokio::test]
async fn test_run_agent_script_before_all_inputs_gen() -> Result<()> {
let runtime = Runtime::new_test_runtime_sandbox_01()?;
let agent = load_test_agent("./agent-script/agent-before-all-inputs-gen.devai", &runtime)?;
let res = run_command_agent(&runtime, agent, None, &RunBaseOptions::default(), true).await?;
let res_value = serde_json::to_value(res)?;
assert!(
matches!(res_value.x_get::<Value>("/outputs/1")?, Value::Null),
"the 2nd input should be null per agent md"
);
assert!(
matches!(res_value.x_get::<Value>("/outputs/3")?, Value::Null),
"the 4th input should be null per agent md"
);
assert!(
matches!(res_value.x_get::<Value>("/outputs/4")?, Value::Null),
"the 5th input should be null per agent md"
);
let res_pretty = res_value.x_pretty()?.to_string();
assert_contains(&res_pretty, r#""data": "Data with input: 'one'""#);
assert_contains(&res_pretty, r#""rexported_inputs": ["#);
Ok(())
}
#[tokio::test]
async fn test_run_agent_script_skip_simple() -> Result<()> {
common_test_run_agent_script_skip(None).await
}
#[tokio::test]
async fn test_run_agent_script_skip_reason() -> Result<()> {
common_test_run_agent_script_skip(Some("Some reason")).await
}
async fn common_test_run_agent_script_skip(reason: Option<&str>) -> Result<()> {
let runtime = Runtime::new_test_runtime_sandbox_01()?;
let reason_str = reason.map(|v| format!("\"{v}\"")).unwrap_or_default();
let fx_inputs = &["one", "two", "three"];
let fx_agent = format!(
r#"
# Data
```lua
if input == "one" then
return devai.skip({reason_str})
end
```
# Output
```lua
return "output for: " .. input
```
"#
);
let agent = load_inline_agent("./dummy/path.devai", fx_agent)?;
let inputs = fx_inputs.iter().map(|v| Value::String(v.to_string())).collect();
let res = run_command_agent(&runtime, agent, Some(inputs), &RunBaseOptions::default(), true)
.await?
.outputs
.ok_or("Should have output result")?;
assert_eq!(res.first().ok_or("Should have input 0")?, &Value::Null);
assert_eq!(
res.get(1)
.ok_or("Should have input 1")?
.as_str()
.ok_or("input 1 should be string")?,
"output for: two"
);
assert_eq!(
res.get(2)
.ok_or("Should have input 2")?
.as_str()
.ok_or("input 2 should be string")?,
"output for: three"
);
Ok(())
}