//! `bxctl memory sync` (M5c spec, section 5): parsed, sent to `memoryd` as the `sync` command, //! its answer printed as it comes, and `memoryd` down reported as for every memory command. Do //! not edit. #[path = "support/temp.rs"] mod temp; use std::os::unix::net::UnixListener; use std::path::{Path, PathBuf}; use std::sync::mpsc; use std::time::Duration; use bxcore::{ Envelope, MemoryAdmin, MemoryAdminReply, MemoryCommand, Message, read_frame, write_frame, }; use bxctl::cli::{Command, USAGE, UsageError, parse}; use bxctl::memory::{Ask, run}; fn args(line: &str) -> Vec { line.split_whitespace().map(str::to_string).collect() } /// A fake `memoryd` answering one request with `text`; hands back what it was sent. fn fake(socket: &Path, text: &str) -> mpsc::Receiver { let listener = UnixListener::bind(socket).unwrap(); let (tx, rx) = mpsc::channel(); let answer = Message::MemoryAdminReply(MemoryAdminReply { text: text.to_string(), }); std::thread::spawn(move || { let (mut stream, _) = listener.accept().unwrap(); let env = read_frame(&mut stream).unwrap(); tx.send(env.msg).unwrap(); let reply = Envelope { v: 1, id: env.id, r#final: true, msg: answer, }; write_frame(&mut stream, &reply).unwrap(); }); rx } #[test] fn sync_parses() { let home = PathBuf::from("/srv/bx"); assert_eq!( parse(&args("memory sync"), &home), Ok(Command::Memory { socket: PathBuf::from("/srv/bx/run/owner-memory/admin.sock"), ask: Ask::Admin(MemoryCommand::Sync, String::new()), }) ); assert_eq!( parse(&args("memory sync --memory-socket /r/m.sock"), &home), Ok(Command::Memory { socket: PathBuf::from("/r/m.sock"), ask: Ask::Admin(MemoryCommand::Sync, String::new()), }) ); assert_eq!(parse(&args("memory sync now"), &home), Err(UsageError)); assert!( USAGE.contains("| reject | sync | tiers [--memory-socket PATH]\n"), "{USAGE}" ); } #[test] fn the_command_is_named_sync_on_the_wire() { assert_eq!( serde_json::to_string(&MemoryCommand::Sync).unwrap(), "\"sync\"" ); } #[test] fn the_answer_is_printed_as_it_comes() { let dir = temp::TempDir::new("bx-mem-sync"); let socket = dir.join("admin.sock"); let report = "honcho sync: 3 decided: 1 sent, 1 waiting, 1 failed, 0 skipped\n\ failed, oldest first:\n \ 2026-09-28T10:00:00.000Z turn 3 of session mm-a status (HTTP 422)"; let seen = fake(&socket, report); let mut out = Vec::new(); let ask = Ask::Admin(MemoryCommand::Sync, String::new()); run(&socket, &ask, Duration::from_secs(5), &mut out).unwrap(); assert_eq!(String::from_utf8(out).unwrap(), format!("{report}\n")); assert_eq!( seen.recv_timeout(Duration::from_secs(5)).unwrap(), Message::MemoryAdmin(MemoryAdmin { command: MemoryCommand::Sync, arg: String::new(), }) ); } #[test] fn memoryd_down_is_reported_as_for_every_memory_command() { let dir = temp::TempDir::new("bx-mem-sync-down"); let ask = Ask::Admin(MemoryCommand::Sync, String::new()); let socket = dir.join("nobody.sock"); let err = run(&socket, &ask, Duration::from_secs(1), &mut Vec::new()).unwrap_err(); assert!( err.starts_with(&format!("cannot reach memoryd at {}: ", socket.display())), "{err}" ); }