//! How long `BrokerPort` waits for `brokerd`, per tool: `[broker] timeout_ms` for every tool but //! `ask_memory`, which waits 180 s or the timeout, whichever is longer (M5c spec, sections 9 and //! 11). `with_ask_wait` shortens the wait here, so no test waits long. Do not edit. #[path = "support/broker.rs"] mod fake; use std::thread; use std::time::Duration; use bxcore::{ToolRequest, ToolResponse}; use fake::{broker, in_ms, request, result, send}; use loopd::broker_port::{ASK_MEMORY_WAIT, BrokerPort, UNAVAILABLE}; use loopd::tools::ToolPort; /// How long the slow fake `brokerd` takes to answer. const SLOW: Duration = Duration::from_millis(1_500); /// A wait shorter than `SLOW`, and one far longer. const SHORT: Duration = Duration::from_millis(300); const LONG: Duration = Duration::from_secs(20); fn ask_memory() -> ToolRequest { ToolRequest { tool: "ask_memory".to_string(), arguments: r#"{"question":"What does the owner prefer?"}"#.to_string(), ..request() } } fn recall() -> ToolRequest { ToolRequest { tool: "recall".to_string(), arguments: r#"{"query":"owner"}"#.to_string(), ..request() } } /// One call of `req`, through a port with `timeout` and an `ask_memory` wait of `ask_wait`, to a /// fake `brokerd` that answers after `SLOW`, optionally after a pending frame that expires at once. fn slow_call( timeout: Duration, ask_wait: Duration, req: &ToolRequest, pending_first: bool, ) -> ToolResponse { let (socket, broker) = broker(move |stream, request| { if pending_first { let pending = ToolResponse::PendingApproval { approval: 3, expires: in_ms(50), }; send(stream, request.id, false, pending); } thread::sleep(SLOW); send(stream, request.id, true, result("the answer")); }); let port = BrokerPort::with_log(socket, timeout, Box::new(|_| {})).with_ask_wait(ask_wait); let response = port.call(req, &mut |_| {}); broker.join().unwrap(); response } fn quiet(timeout: Duration) -> BrokerPort { BrokerPort::with_log("unused.sock".into(), timeout, Box::new(|_| {})) } fn unavailable() -> ToolResponse { ToolResponse::Failed { message: UNAVAILABLE.to_string(), } } #[test] fn the_wait_for_each_tool() { assert_eq!(ASK_MEMORY_WAIT, Duration::from_secs(180)); let port = BrokerPort::new("unused.sock".into(), Duration::from_secs(120)); assert_eq!(port.timeout_for("ask_memory"), ASK_MEMORY_WAIT); for tool in ["recall", "memory", "read_file", "shell", "clock", "ask"] { assert_eq!(port.timeout_for(tool), Duration::from_secs(120), "{tool}"); } // A configured timeout longer than 180 s is kept for ask_memory too. let port = BrokerPort::new("unused.sock".into(), Duration::from_secs(300)); assert_eq!(port.timeout_for("ask_memory"), Duration::from_secs(300)); let port = quiet(Duration::from_secs(120)); assert_eq!( port.timeout_for("ask_memory"), ASK_MEMORY_WAIT, "with_log as new" ); assert_eq!(port.timeout_for("recall"), Duration::from_secs(120)); let port = quiet(SHORT).with_ask_wait(LONG); assert_eq!(port.timeout_for("ask_memory"), LONG); assert_eq!(port.timeout_for("recall"), SHORT); let port = quiet(LONG).with_ask_wait(SHORT); assert_eq!( port.timeout_for("ask_memory"), LONG, "the longer of the two" ); } /// A slow answer reaches `ask_memory`, whose wait is long, and not `recall` or `read_file`, /// which have the short timeout. #[test] fn a_slow_answer_reaches_ask_memory_only() { assert_eq!( slow_call(SHORT, LONG, &ask_memory(), false), result("the answer") ); assert_eq!(slow_call(SHORT, LONG, &recall(), false), unavailable()); assert_eq!(slow_call(SHORT, LONG, &request(), false), unavailable()); } /// With a longer timeout than `ask_memory`'s own wait, `ask_memory` waits the timeout. #[test] fn a_longer_timeout_is_kept_for_ask_memory() { assert_eq!( slow_call(LONG, SHORT, &ask_memory(), false), result("the answer") ); } /// After a pending frame, the port waits for the expiry plus the call's own wait: for /// `ask_memory`, its long wait, not the short timeout. #[test] fn after_a_pending_frame_ask_memory_keeps_its_wait() { assert_eq!( slow_call(SHORT, LONG, &ask_memory(), true), result("the answer") ); assert_eq!(slow_call(SHORT, LONG, &recall(), true), unavailable()); }