use sgclaw::pipe::{ Action, AgentMessage, BrowserMessage, ExecutionSurfaceKind, SecurityFields, Timing, }; #[test] fn browser_init_round_trip_uses_frozen_wire_format() { let raw = r#"{"type":"init","version":"1.0","hmac_seed":"0123456789abcdef","capabilities":["browser_action"]}"#; let message: BrowserMessage = serde_json::from_str(raw).unwrap(); assert!(matches!( message, BrowserMessage::Init { ref version, ref hmac_seed, ref capabilities } if version == "1.0" && hmac_seed == "0123456789abcdef" && *capabilities == vec!["browser_action".to_string()] )); assert_eq!(serde_json::to_string(&message).unwrap(), raw); } #[test] fn browser_lifecycle_messages_use_frozen_wire_tags() { let connect_raw = r#"{"type":"connect"}"#; let start_raw = r#"{"type":"start"}"#; let stop_raw = r#"{"type":"stop"}"#; let connect: BrowserMessage = serde_json::from_str(connect_raw).unwrap(); let start: BrowserMessage = serde_json::from_str(start_raw).unwrap(); let stop: BrowserMessage = serde_json::from_str(stop_raw).unwrap(); assert_eq!(connect, BrowserMessage::Connect); assert_eq!(start, BrowserMessage::Start); assert_eq!(stop, BrowserMessage::Stop); assert_eq!(serde_json::to_string(&connect).unwrap(), connect_raw); assert_eq!(serde_json::to_string(&start).unwrap(), start_raw); assert_eq!(serde_json::to_string(&stop).unwrap(), stop_raw); } #[test] fn command_serializes_action_and_security_fields() { let message = AgentMessage::Command { seq: 1, action: Action::GetText, params: serde_json::json!({ "selector": "#submit" }), security: SecurityFields { expected_domain: "oa.example.com".to_string(), hmac: "abc123".to_string(), }, }; let raw = serde_json::to_string(&message).unwrap(); assert!(raw.contains(r#""type":"command""#)); assert!(raw.contains(r#""action":"getText""#)); assert!(raw.contains(r#""expected_domain":"oa.example.com""#)); } #[test] fn agent_status_changed_serializes_with_expected_tag() { let raw = serde_json::to_string(&AgentMessage::StatusChanged { state: "started".to_string(), }) .unwrap(); assert_eq!(raw, r#"{"type":"status_changed","state":"started"}"#); } #[test] fn response_deserializes_timing_and_payload() { let raw = r#"{"type":"response","seq":7,"success":true,"data":{"text":"提交成功"},"aom_snapshot":[],"timing":{"queue_ms":2,"exec_ms":38}}"#; let message: BrowserMessage = serde_json::from_str(raw).unwrap(); assert_eq!( message, BrowserMessage::Response { seq: 7, success: true, data: serde_json::json!({"text": "提交成功"}), aom_snapshot: vec![], timing: Timing { queue_ms: 2, exec_ms: 38, }, } ); } #[test] fn submit_task_exposes_browser_context_without_implying_browser_only_runtime() { let message = BrowserMessage::SubmitTask { instruction: "统计一下知乎热榜".to_string(), conversation_id: "conversation-1".to_string(), messages: vec![], page_url: "https://www.zhihu.com/hot".to_string(), page_title: "知乎热榜".to_string(), }; let context = message.browser_context().expect("browser context"); let surface = message .requested_surface_metadata() .expect("surface metadata"); assert_eq!(context.page_url, "https://www.zhihu.com/hot"); assert_eq!(context.page_title, "知乎热榜"); assert_eq!(surface.kind, ExecutionSurfaceKind::PrivilegedBrowserPipe); assert!(surface.privileged); assert!(!surface.defines_runtime_identity); } #[test] fn supported_actions_include_browser_script_execution() { let supported = sgclaw::pipe::supported_actions(); assert!(supported.iter().any(|action| action.as_str() == "eval")); }