focus/crates/focus-tools/tests/shell_tests.rs

98 lines
3.0 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! `shell` 工具的单元测试unix 分支Windows 分支在有 Windows 的 CI 上跑)。
//! Unit tests for the `shell` tool (unix branch; the windows branch runs on
//! Windows CI).
use focus_core::tool::Tool;
use focus_core::tool::ToolUpdate;
use focus_json::JsonValue;
use focus_tools::ShellTool;
use std::fs;
use std::path::PathBuf;
use std::sync::{Arc, Mutex};
fn temp_dir(tag: &str) -> PathBuf {
let d = std::env::temp_dir().join(format!("focus-tools-shell-{}-{}", tag, std::process::id()));
let _ = fs::remove_dir_all(&d);
fs::create_dir_all(&d).unwrap();
d
}
fn cmd_args(command: &str) -> JsonValue {
let mut args = JsonValue::obj();
args.insert("command", command.into()).ok();
args
}
#[cfg(unix)]
#[test]
fn runs_bash_and_captures_stdout() {
let dir = temp_dir("stdout");
let tool = ShellTool::new(&dir);
let r = tool.execute("id", &cmd_args("echo hello"), None).unwrap();
let text = r.content[0].as_text().unwrap().text.clone();
assert_eq!(text.trim(), "hello");
assert_eq!(r.details.get_num("exitCode"), Some(0.0));
assert_eq!(r.details.get_bool("timedOut"), Some(false));
}
#[cfg(unix)]
#[test]
fn captures_stderr_and_exit_code() {
let dir = temp_dir("stderr");
let tool = ShellTool::new(&dir);
let r = tool
.execute("id", &cmd_args("echo oops >&2; exit 3"), None)
.unwrap();
assert_eq!(r.details.get_num("exitCode"), Some(3.0));
let stderr = r.details.get_str("stderr").unwrap_or("");
assert!(stderr.contains("oops"), "got: {}", stderr);
}
#[cfg(unix)]
#[test]
fn times_out_and_kills() {
let dir = temp_dir("timeout");
let tool = ShellTool::new(&dir);
let mut args = cmd_args("sleep 5");
args.insert("timeoutMs", 200u64.into()).ok();
let r = tool.execute("id", &args, None).unwrap();
assert_eq!(r.details.get_bool("timedOut"), Some(true));
let text = r.content[0].as_text().unwrap().text.clone();
assert!(text.contains("timed out"), "got: {}", text);
}
#[cfg(unix)]
#[test]
fn streams_updates_line_by_line() {
let dir = temp_dir("updates");
let tool = ShellTool::new(&dir);
let lines: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(Vec::new()));
let sink: Box<dyn Fn(ToolUpdate) + Send + Sync> = {
let lines = lines.clone();
Box::new(move |u: ToolUpdate| {
if let Some(t) = u.content[0].as_text() {
lines.lock().unwrap().push(t.text.clone());
}
})
};
let r = tool
.execute("id", &cmd_args("printf 'a\\nb\\nc\\n'"), Some(&*sink))
.unwrap();
assert_eq!(r.details.get_num("exitCode"), Some(0.0));
let got = lines.lock().unwrap();
assert_eq!(
*got,
vec!["a".to_string(), "b".to_string(), "c".to_string()]
);
}
#[cfg(unix)]
#[test]
fn runs_in_project_root() {
let dir = temp_dir("cwd");
let tool = ShellTool::new(&dir);
let r = tool.execute("id", &cmd_args("pwd"), None).unwrap();
let text = r.content[0].as_text().unwrap().text.clone();
assert_eq!(text.trim(), dir.display().to_string());
}