use std::io::{Cursor, Result as IoResult, Write}; use std::sync::{Arc, Mutex}; use std::time::Duration; use sgclaw::pipe::{ perform_handshake, Action, AgentMessage, BrowserPipeTool, StdioTransport, Timing, }; use sgclaw::security::MacPolicy; #[derive(Clone, Default)] struct SharedBuffer { inner: Arc>>, } impl SharedBuffer { fn snapshot(&self) -> String { String::from_utf8(self.inner.lock().unwrap().clone()).unwrap() } } impl Write for SharedBuffer { fn write(&mut self, buf: &[u8]) -> IoResult { self.inner.lock().unwrap().extend_from_slice(buf); Ok(buf.len()) } fn flush(&mut self) -> IoResult<()> { Ok(()) } } #[test] fn handshake_and_command_flow_work_over_json_line_transport() { let reader = Cursor::new( concat!( r#"{"type":"init","version":"1.0","hmac_seed":"0123456789abcdef","capabilities":["browser_action"]}"#, "\n", r#"{"type":"response","seq":1,"success":true,"data":{"text":"提亀成功"},"aom_snapshot":[],"timing":{"queue_ms":2,"exec_ms":38}}"#, "\n" ) .as_bytes() .to_vec(), ); let writer = SharedBuffer::default(); let captured = writer.clone(); let transport = Arc::new(StdioTransport::new(reader, writer)); let handshake = perform_handshake(transport.as_ref(), Duration::from_secs(1)).unwrap(); let policy = MacPolicy::from_json_str( r#"{ "version": "1.0", "domains": { "allowed": ["oa.example.com"] }, "pipe_actions": { "allowed": ["click", "type", "navigate", "getText"], "blocked": [] } }"#, ) .unwrap(); let tool = BrowserPipeTool::new(transport, policy, handshake.session_key) .with_response_timeout(Duration::from_secs(1)); let result = tool .invoke( Action::Click, serde_json::json!({ "selector": "#submit" }), "oa.example.com", ) .unwrap(); let written = captured.snapshot(); assert_eq!( result.timing, Timing { queue_ms: 2, exec_ms: 38 } ); assert!(written.contains(r#""type":"init_ack""#)); assert!(written.contains(r#""type":"command""#)); assert!(written.contains(r#""action":"click""#)); let lines: Vec<&str> = written.lines().collect(); let command: AgentMessage = serde_json::from_str(lines[1]).unwrap(); assert!(matches!( command, AgentMessage::Command { seq, .. } if seq == 1 )); }