mod common; use std::sync::Arc; use std::time::Duration; use common::MockTransport; use sgclaw::agent::runtime::{browser_action_tool_definition, execute_task_with_provider}; use sgclaw::llm::{ChatMessage, LlmError, LlmProvider, ToolDefinition, ToolFunctionCall}; use sgclaw::pipe::{Action, AgentMessage, BrowserMessage, BrowserPipeTool, Timing}; use sgclaw::security::MacPolicy; struct FakeProvider { calls: Vec, } impl LlmProvider for FakeProvider { fn chat( &self, _messages: &[ChatMessage], _tools: &[ToolDefinition], ) -> Result, LlmError> { Ok(self.calls.clone()) } } fn test_policy() -> MacPolicy { MacPolicy::from_json_str( r#"{ "version": "1.0", "domains": { "allowed": ["www.baidu.com"] }, "pipe_actions": { "allowed": ["click", "type", "navigate", "getText"], "blocked": [] } }"#, ) .unwrap() } #[test] fn browser_action_tool_definition_uses_expected_name() { let tool = browser_action_tool_definition(); assert_eq!(tool.name, "browser_action"); assert_eq!(tool.parameters["required"][0], "action"); assert_eq!(tool.parameters["required"][1], "expected_domain"); } #[test] fn runtime_executes_provider_tool_calls_and_returns_summary() { 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: 10, }, }, BrowserMessage::Response { seq: 2, success: true, data: serde_json::json!({ "typed": true }), aom_snapshot: vec![], timing: Timing { queue_ms: 1, exec_ms: 10, }, }, ])); let browser_tool = BrowserPipeTool::new( transport.clone(), test_policy(), vec![1, 2, 3, 4, 5, 6, 7, 8], ) .with_response_timeout(Duration::from_secs(1)); let provider = FakeProvider { calls: vec![ ToolFunctionCall { id: "call-1".to_string(), name: "browser_action".to_string(), arguments: serde_json::json!({ "action": "navigate", "expected_domain": "www.baidu.com", "url": "https://www.baidu.com" }), }, ToolFunctionCall { id: "call-2".to_string(), name: "browser_action".to_string(), arguments: serde_json::json!({ "action": "type", "expected_domain": "www.baidu.com", "selector": "#kw", "text": "天气", "clear_first": true }), }, ], }; let summary = execute_task_with_provider( transport.as_ref(), &browser_tool, &provider, "打开百度搜索天气", ) .unwrap(); let sent = transport.sent_messages(); assert_eq!(summary, "已通过 Agent 执行任务: 打开百度搜索天气"); assert!(matches!( &sent[0], AgentMessage::LogEntry { level, message } if level == "info" && message == "navigate www.baidu.com" )); assert!(matches!( &sent[1], AgentMessage::Command { seq, action, .. } if *seq == 1 && action == &Action::Navigate )); assert!(matches!( &sent[2], AgentMessage::LogEntry { level, message } if level == "info" && message == "type www.baidu.com" )); assert!(matches!( &sent[3], AgentMessage::Command { seq, action, .. } if *seq == 2 && action == &Action::Type )); }