Files
skill-lib/tests/runtime_task_flow_test.rs
木炎 6aad2ce48e feat: restore zhihu browser skills
Reconnect the recovered Zhihu skill flows to the live browser runtime and resolve their resources relative to the executable so they work outside the repo root.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 14:29:38 +08:00

236 lines
6.9 KiB
Rust

mod common;
use std::sync::Arc;
use std::time::Duration;
use common::MockTransport;
use sgclaw::agent::handle_browser_message;
use sgclaw::pipe::{Action, AgentMessage, BrowserMessage, BrowserPipeTool, Timing};
use sgclaw::security::MacPolicy;
fn test_policy() -> MacPolicy {
MacPolicy::from_json_str(
r#"{
"version": "1.0",
"domains": { "allowed": ["oa.example.com", "www.baidu.com", "www.zhihu.com", "zhuanlan.zhihu.com"] },
"pipe_actions": {
"allowed": ["click", "type", "navigate", "getText", "getHtml", "waitForSelector", "scrollTo"],
"blocked": ["eval", "executeJsInPage"]
}
}"#,
)
.unwrap()
}
#[test]
fn submit_task_sends_three_commands_and_finishes_with_task_complete() {
let transport = Arc::new(MockTransport::new(vec![
BrowserMessage::Response {
seq: 1,
success: true,
data: serde_json::json!({ "navigated": true }),
aom_snapshot: vec![],
timing: Timing {
queue_ms: 1,
exec_ms: 20,
},
},
BrowserMessage::Response {
seq: 2,
success: true,
data: serde_json::json!({ "typed": true }),
aom_snapshot: vec![],
timing: Timing {
queue_ms: 1,
exec_ms: 20,
},
},
BrowserMessage::Response {
seq: 3,
success: true,
data: serde_json::json!({ "clicked": true }),
aom_snapshot: vec![],
timing: Timing {
queue_ms: 1,
exec_ms: 20,
},
},
]));
let tool = BrowserPipeTool::new(
transport.clone(),
test_policy(),
vec![1, 2, 3, 4, 5, 6, 7, 8],
)
.with_response_timeout(Duration::from_secs(1));
handle_browser_message(
transport.as_ref(),
&tool,
BrowserMessage::SubmitTask {
instruction: "打开百度搜索天气".to_string(),
conversation_id: String::new(),
messages: vec![],
page_url: String::new(),
page_title: String::new(),
},
)
.unwrap();
let sent = transport.sent_messages();
assert_eq!(sent.len(), 8);
assert!(matches!(
&sent[0],
AgentMessage::LogEntry { level, message }
if level == "mode" && message == "deterministic_planner"
));
assert!(matches!(
&sent[1],
AgentMessage::LogEntry { level, message }
if level == "info" && message == "navigate https://www.baidu.com"
));
assert!(matches!(
&sent[2],
AgentMessage::Command { seq, action, .. }
if *seq == 1 && action == &Action::Navigate
));
assert!(matches!(
&sent[3],
AgentMessage::LogEntry { level, message }
if level == "info" && message == "type 天气 into #kw"
));
assert!(matches!(
&sent[4],
AgentMessage::Command { seq, action, .. }
if *seq == 2 && action == &Action::Type
));
assert!(matches!(
&sent[5],
AgentMessage::LogEntry { level, message }
if level == "info" && message == "click #su"
));
assert!(matches!(
&sent[6],
AgentMessage::Command { seq, action, .. }
if *seq == 3 && action == &Action::Click
));
assert!(matches!(
&sent[7],
AgentMessage::TaskComplete { success, summary }
if *success && summary == "已在百度搜索天气"
));
}
#[test]
fn explicit_zhihu_skill_short_circuits_before_planner_fallback() {
let transport = Arc::new(MockTransport::new(vec![BrowserMessage::Response {
seq: 1,
success: true,
data: serde_json::json!({ "url": "https://www.zhihu.com/creator/analytics/work/all" }),
aom_snapshot: vec![],
timing: Timing {
queue_ms: 1,
exec_ms: 20,
},
}]));
let tool = BrowserPipeTool::new(
transport.clone(),
test_policy(),
vec![1, 2, 3, 4, 5, 6, 7, 8],
)
.with_response_timeout(Duration::from_secs(1));
handle_browser_message(
transport.as_ref(),
&tool,
BrowserMessage::SubmitTask {
instruction:
r#"skill:zhihu_navigate {"page":"content_analysis","ensure_loaded":false}"#
.to_string(),
conversation_id: String::new(),
messages: vec![],
page_url: String::new(),
page_title: String::new(),
},
)
.unwrap();
let sent = transport.sent_messages();
assert_eq!(sent.len(), 3);
assert!(matches!(
&sent[0],
AgentMessage::LogEntry { level, message }
if level == "info"
&& message == "navigate https://www.zhihu.com/creator/analytics/work/all"
));
assert!(matches!(
&sent[1],
AgentMessage::Command { seq, action, security, .. }
if *seq == 1
&& action == &Action::Navigate
&& security.expected_domain == "www.zhihu.com"
));
assert!(matches!(
&sent[2],
AgentMessage::TaskComplete { success, summary }
if *success
&& summary
== "知乎页面已打开:内容分析 (https://www.zhihu.com/creator/analytics/work/all)"
));
}
#[test]
fn natural_language_zhihu_navigation_short_circuits_before_planner_fallback() {
let transport = Arc::new(MockTransport::new(vec![BrowserMessage::Response {
seq: 1,
success: true,
data: serde_json::json!({ "url": "https://www.zhihu.com/" }),
aom_snapshot: vec![],
timing: Timing {
queue_ms: 1,
exec_ms: 20,
},
}]));
let tool = BrowserPipeTool::new(
transport.clone(),
test_policy(),
vec![1, 2, 3, 4, 5, 6, 7, 8],
)
.with_response_timeout(Duration::from_secs(1));
handle_browser_message(
transport.as_ref(),
&tool,
BrowserMessage::SubmitTask {
instruction: "打开知乎首页".to_string(),
conversation_id: String::new(),
messages: vec![],
page_url: String::new(),
page_title: String::new(),
},
)
.unwrap();
let sent = transport.sent_messages();
assert_eq!(sent.len(), 3);
assert!(matches!(
&sent[0],
AgentMessage::LogEntry { level, message }
if level == "info" && message == "navigate https://www.zhihu.com/"
));
assert!(matches!(
&sent[1],
AgentMessage::Command { seq, action, security, .. }
if *seq == 1
&& action == &Action::Navigate
&& security.expected_domain == "www.zhihu.com"
));
assert!(matches!(
&sent[2],
AgentMessage::TaskComplete { success, summary }
if *success && summary == "知乎页面已打开:首页 (https://www.zhihu.com/)"
));
}