//! How long `brokerd` waits for `memoryd`, per tool: 30 s for `recall` and `memory`, 170 s for //! `ask_memory`, whose two Honcho chats have 150 s each (M5c spec, sections 9 and 11). The waits //! are shortened here with `with_ask_wait` and `new`'s own wait, so no test waits long. Do not //! edit. #[path = "support/build.rs"] mod build; #[path = "support/tmp.rs"] mod tmp; use std::os::unix::net::UnixListener; use std::path::Path; use std::time::Duration; use brokerd::args::ToolName; use brokerd::memory_port::{ASK_MEMORY_WAIT, MEMORY_WAIT, MemoryPort, UNREACHABLE}; use brokerd::policy::decide; use brokerd::runner::run; use build::{allowed, now, private, request, set}; use bxcore::{DataClass, Envelope, Message, ToolRequest, ToolResponse, read_frame, write_frame}; use tmp::TempDir; /// How long the slow fake `memoryd` 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 found() -> ToolResponse { ToolResponse::Result { content: "own: memory ยท The owner prefers UTC.".to_string(), class: DataClass::Private, untrusted: false, truncated: false, } } /// A fake `memoryd` that answers one call after `SLOW`. A failed write is ignored: the port has /// stopped waiting, which is what some tests expect. fn slow_memoryd(socket: &Path) { let listener = UnixListener::bind(socket).unwrap(); std::thread::spawn(move || { let Ok((mut stream, _)) = listener.accept() else { return; }; let Ok(env) = read_frame(&mut stream) else { return; }; std::thread::sleep(SLOW); let reply = Envelope { v: 1, id: env.id, r#final: true, msg: Message::ToolResponse(found()), }; let _ = write_frame(&mut stream, &reply); }); } /// One allowed call of `req` through `port`, to a slow fake `memoryd` of its own. fn ask( dir: &TempDir, name: &str, port: impl Fn(&Path) -> MemoryPort, req: ToolRequest, ) -> ToolResponse { let socket = dir.path().join(name); slow_memoryd(&socket); let decision = allowed(decide(req, &set(vec![]), private(), now())); run(decision, &port(&socket)) } fn unreachable() -> ToolResponse { ToolResponse::Failed { message: UNREACHABLE.to_string(), } } const QUESTION: &str = r#"{"question":"What does the owner prefer?"}"#; const QUERY: &str = r#"{"query":"owner"}"#; #[test] fn the_waits_and_which_tool_gets_which() { assert_eq!(MEMORY_WAIT, Duration::from_secs(30)); assert_eq!(ASK_MEMORY_WAIT, Duration::from_secs(170)); let port = MemoryPort::new("memory.sock".into(), MEMORY_WAIT); assert_eq!(port.wait_for(ToolName::Recall), MEMORY_WAIT); assert_eq!(port.wait_for(ToolName::Memory), MEMORY_WAIT); assert_eq!(port.wait_for(ToolName::AskMemory), ASK_MEMORY_WAIT); let port = MemoryPort::new("memory.sock".into(), SHORT); assert_eq!(port.wait_for(ToolName::Recall), SHORT); assert_eq!(port.wait_for(ToolName::Memory), SHORT); assert_eq!(port.wait_for(ToolName::AskMemory), ASK_MEMORY_WAIT); let port = MemoryPort::new("memory.sock".into(), MEMORY_WAIT).with_ask_wait(LONG); assert_eq!(port.wait_for(ToolName::Recall), MEMORY_WAIT); assert_eq!(port.wait_for(ToolName::Memory), MEMORY_WAIT); assert_eq!(port.wait_for(ToolName::AskMemory), LONG); } /// A slow answer reaches `ask_memory`, whose wait is long, and not `recall` or `memory`, whose /// wait is short: the wait is chosen by the call's tool. #[test] fn a_slow_answer_reaches_ask_memory_only_when_its_wait_is_longer() { let dir = TempDir::new("wait-ask"); let port = |socket: &Path| MemoryPort::new(socket.to_path_buf(), SHORT).with_ask_wait(LONG); assert_eq!( ask(&dir, "a.sock", port, request("ask_memory", QUESTION)), found() ); assert_eq!( ask(&dir, "r.sock", port, request("recall", QUERY)), unreachable() ); let note = r#"{"action":"note","text":"x"}"#; assert_eq!( ask(&dir, "m.sock", port, request("memory", note)), unreachable() ); } /// And the other way round: a short wait for `ask_memory` gives up, while `recall` waits. #[test] fn a_slow_answer_reaches_recall_only_when_its_wait_is_longer() { let dir = TempDir::new("wait-recall"); let port = |socket: &Path| MemoryPort::new(socket.to_path_buf(), LONG).with_ask_wait(SHORT); assert_eq!(ask(&dir, "r.sock", port, request("recall", QUERY)), found()); assert_eq!( ask(&dir, "a.sock", port, request("ask_memory", QUESTION)), unreachable() ); }