mod common; use std::sync::Arc; use std::time::Duration; use common::MockTransport; 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", "erp.example.com"] }, "pipe_actions": { "allowed": ["click", "type", "navigate", "getText"], "blocked": ["eval", "executeJsInPage"] } }"#, ) .unwrap() } #[test] fn browser_tool_signs_and_sends_command_then_waits_for_response() { let transport = Arc::new(MockTransport::new(vec![BrowserMessage::Response { seq: 1, success: true, data: serde_json::json!({"text": "ok"}), 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)); let result = tool .invoke( Action::Click, serde_json::json!({ "selector": "#submit" }), "oa.example.com", ) .unwrap(); let sent = transport.sent_messages(); assert_eq!(result.seq, 1); assert_eq!(result.data, serde_json::json!({"text": "ok"})); assert_eq!(sent.len(), 1); assert!(matches!( &sent[0], AgentMessage::Command { seq, action, params, security } if *seq == 1 && action == &Action::Click && params == &serde_json::json!({"selector": "#submit"}) && security.expected_domain == "oa.example.com" && !security.hmac.is_empty() )); } #[test] fn browser_tool_rejects_action_when_mac_policy_blocks_it() { let transport = Arc::new(MockTransport::new(vec![])); let tool = BrowserPipeTool::new(transport, test_policy(), vec![1, 2, 3, 4]); let err = tool .invoke( Action::GetHtml, serde_json::json!({ "selector": "body" }), "oa.example.com", ) .unwrap_err(); assert!(err.to_string().contains("action is not allowed")); }