优化: run_command 实时流式 + 审批浮窗修复 + 文档探索闭环(fetch_url+obscura引导+model_fetch兼容+prompt策略+厂商预设)
This commit is contained in:
@@ -9,7 +9,7 @@
|
||||
//!
|
||||
//! 注:execute 逻辑本身未改动,此文件为零行为变更的纯新增测试。
|
||||
|
||||
use df_execute::shell::{execute, ShellRequest, ShellType};
|
||||
use df_execute::shell::{execute, execute_streaming, ShellRequest, ShellType, StreamKind};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// 平台默认 ShellType(对齐 shell.rs:31 Default impl:Windows→Cmd, 非 Windows→Sh)
|
||||
@@ -159,3 +159,81 @@ async fn execute_working_dir() {
|
||||
// 清理
|
||||
let _ = std::fs::remove_dir_all(&tmp_for_cleanup);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// execute_streaming 流式测试
|
||||
// ============================================================
|
||||
|
||||
/// 流式:stdout 多行逐行回调,且 ShellResult 完整(行数对齐 + exit_code=0)。
|
||||
///
|
||||
/// 治 run_command 黑盒:验证 spawn 后逐行回调 vs 一次性返回的等价性(行内容 + 完整结果)。
|
||||
#[tokio::test]
|
||||
async fn streaming_stdout_lines_callback() {
|
||||
// 多行输出:Cmd 用多个 echo(用 & 串联无依赖),Sh 用 printf 多行
|
||||
let cmd = if cfg!(windows) {
|
||||
"@echo line1 & @echo line2 & @echo line3"
|
||||
} else {
|
||||
"printf 'line1\\nline2\\nline3\\n'"
|
||||
};
|
||||
let mut lines: Vec<(StreamKind, String)> = Vec::new();
|
||||
let res = execute_streaming(req(cmd), |kind, line| {
|
||||
lines.push((kind, line.to_string()));
|
||||
})
|
||||
.await
|
||||
.expect("execute_streaming 应返回 Ok");
|
||||
|
||||
assert_eq!(res.exit_code, Some(0), "成功命令 exit_code 应为 0");
|
||||
// stdout 应含三行(line1/line2/line3)
|
||||
assert!(res.stdout.contains("line1"), "stdout 应含 line1,实际: {:?}", res.stdout);
|
||||
assert!(res.stdout.contains("line3"), "stdout 应含 line3,实际: {:?}", res.stdout);
|
||||
// 回调收到的 stdout 行应含三行(过滤 stderr 干扰:Cmd 无 stderr,Sh 无 stderr)
|
||||
let stdout_lines: Vec<&String> = lines.iter()
|
||||
.filter(|(k, _)| *k == StreamKind::Stdout)
|
||||
.map(|(_, l)| l)
|
||||
.collect();
|
||||
assert!(
|
||||
stdout_lines.iter().any(|l| l.contains("line1")),
|
||||
"回调应收到含 line1 的 stdout 行,实际: {:?}", stdout_lines
|
||||
);
|
||||
assert!(
|
||||
stdout_lines.iter().any(|l| l.contains("line3")),
|
||||
"回调应收到含 line3 的 stdout 行,实际: {:?}", stdout_lines
|
||||
);
|
||||
}
|
||||
|
||||
/// 流式:超时仍生效(timeout_secs=1 + 长睡命令,返回 Err)。
|
||||
#[tokio::test]
|
||||
async fn streaming_timeout_returns_err() {
|
||||
let sleep_cmd = if cfg!(windows) {
|
||||
"ping -n 5 127.0.0.1 > nul".to_string()
|
||||
} else {
|
||||
"sleep 5".to_string()
|
||||
};
|
||||
let request = ShellRequest {
|
||||
command: sleep_cmd,
|
||||
working_dir: None,
|
||||
env: HashMap::new(),
|
||||
timeout_secs: Some(1),
|
||||
shell_type: Some(default_shell()),
|
||||
};
|
||||
let result = execute_streaming(request, |_, _| {}).await;
|
||||
assert!(result.is_err(), "超时应返回 Err,实际: {:?}", result.as_ref().err());
|
||||
let msg = result.unwrap_err().to_string();
|
||||
assert!(
|
||||
msg.contains("超时") || msg.to_lowercase().contains("timeout"),
|
||||
"错误信息应含超时提示,实际: {}",
|
||||
msg
|
||||
);
|
||||
}
|
||||
|
||||
/// 流式:非零退出仍返回 Ok + exit_code 非 0(对齐 execute 语义)。
|
||||
#[tokio::test]
|
||||
async fn streaming_nonzero_exit() {
|
||||
let mut callbacks = 0u32;
|
||||
let res = execute_streaming(req("exit 1"), |_, _| { callbacks += 1; })
|
||||
.await
|
||||
.expect("非零退出应仍返回 Ok");
|
||||
assert_ne!(res.exit_code, Some(0), "exit 1 的 exit_code 应非 0");
|
||||
// exit 1 无输出,回调可为 0 次(无行)——不强制断言次数,只确认无 panic
|
||||
let _ = callbacks;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user