bxctl memory sync: the Honcho ledger's counts and failed items
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
//! `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<String> {
|
||||
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<Message> {
|
||||
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 <id> | sync [--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}"
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user