About 10,000 test leftovers had built up in titan's /tmp. loopd's Home and FakeServer, its fake broker's socket, bxctl's temp sockets, fake loopd and command homes, inferproxy's socket, and a few brokerd and toolkit tests created uniquely named directories or sockets and never removed them, or removed them by hand at the end, which a failing test skips. Each is now a guard removed on drop (bxctl gains tests/support/temp.rs). Two races also left gatewayd homes behind: the test Running only signalled its server to stop, and two Stage structs declared the home first, so it was removed while the server could still write into it. Running's drop now waits for the thread (with a 5-second limit), and every home is declared last. Checked: make gate twice, then the gate and three rounds of gatewayd and loopd tests; no new entry in /tmp; 944 tests pass. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
185 lines
5.4 KiB
Rust
185 lines
5.4 KiB
Rust
//! `bxctl reindex` and `bxctl memory`: parsing, what is sent to `memoryd`, and what is printed
|
|
//! (M5a spec, section 10). 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 bxctl::cli::{Command, UsageError, parse};
|
|
use bxctl::memory::{Ask, run};
|
|
use proto::{
|
|
Envelope, ErrorCode, MemoryAdminReply, MemoryCommand, Message, Reindexed, WireError,
|
|
read_frame, write_frame,
|
|
};
|
|
|
|
fn args(line: &str) -> Vec<String> {
|
|
line.split_whitespace().map(str::to_string).collect()
|
|
}
|
|
|
|
fn home() -> PathBuf {
|
|
PathBuf::from("/srv/bx")
|
|
}
|
|
|
|
fn memory(ask: Ask) -> Result<Command, UsageError> {
|
|
Ok(Command::Memory {
|
|
socket: PathBuf::from("/srv/bx/run/owner-memory/admin.sock"),
|
|
ask,
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn the_commands_parse() {
|
|
let h = home();
|
|
assert_eq!(parse(&args("reindex"), &h), memory(Ask::Reindex));
|
|
assert_eq!(
|
|
parse(&args("memory log"), &h),
|
|
memory(Ask::Admin(MemoryCommand::Log, String::new()))
|
|
);
|
|
assert_eq!(
|
|
parse(&args("memory log 3"), &h),
|
|
memory(Ask::Admin(MemoryCommand::Log, "3".to_string()))
|
|
);
|
|
assert_eq!(
|
|
parse(&args("memory undo 2026-09-24-0007"), &h),
|
|
memory(Ask::Admin(
|
|
MemoryCommand::Undo,
|
|
"2026-09-24-0007".to_string()
|
|
))
|
|
);
|
|
assert_eq!(
|
|
parse(&args("memory pending"), &h),
|
|
memory(Ask::Admin(MemoryCommand::Pending, String::new()))
|
|
);
|
|
assert_eq!(
|
|
parse(&args("memory accept p-1"), &h),
|
|
memory(Ask::Admin(MemoryCommand::Accept, "p-1".to_string()))
|
|
);
|
|
assert_eq!(
|
|
parse(&args("memory reject p-1"), &h),
|
|
memory(Ask::Admin(MemoryCommand::Reject, "p-1".to_string()))
|
|
);
|
|
assert_eq!(
|
|
parse(&args("reindex --memory-socket /r/m.sock"), &h),
|
|
Ok(Command::Memory {
|
|
socket: PathBuf::from("/r/m.sock"),
|
|
ask: Ask::Reindex
|
|
})
|
|
);
|
|
assert_eq!(
|
|
parse(&args("memory --memory-socket /r/m.sock undo x"), &h),
|
|
Ok(Command::Memory {
|
|
socket: PathBuf::from("/r/m.sock"),
|
|
ask: Ask::Admin(MemoryCommand::Undo, "x".to_string())
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn bad_lines_are_usage_errors() {
|
|
for line in [
|
|
"memory",
|
|
"memory log ten",
|
|
"memory log 1 2",
|
|
"memory undo",
|
|
"memory undo a b",
|
|
"memory forget x",
|
|
"memory pending x",
|
|
"reindex now",
|
|
"reindex --memory-socket",
|
|
"reindex --memory-socket /a --memory-socket /b",
|
|
] {
|
|
assert_eq!(parse(&args(line), &home()), Err(UsageError), "{line}");
|
|
}
|
|
}
|
|
|
|
/// A fake `memoryd` answering one request with `answer`; hands back what it was sent.
|
|
fn fake(socket: &Path, answer: Message) -> mpsc::Receiver<Message> {
|
|
let listener = UnixListener::bind(socket).unwrap();
|
|
let (tx, rx) = mpsc::channel();
|
|
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
|
|
}
|
|
|
|
fn dir(tag: &str) -> temp::TempDir {
|
|
temp::TempDir::new(&format!("bx-mem-{tag}"))
|
|
}
|
|
|
|
#[test]
|
|
fn reindex_prints_the_counts() {
|
|
let d = dir("reindex");
|
|
let socket = d.join("admin.sock");
|
|
let seen = fake(
|
|
&socket,
|
|
Message::Reindexed(Reindexed {
|
|
logs: 42,
|
|
records: 1208,
|
|
notes: 7,
|
|
}),
|
|
);
|
|
let mut out = Vec::new();
|
|
run(&socket, &Ask::Reindex, Duration::from_secs(5), &mut out).unwrap();
|
|
assert_eq!(
|
|
String::from_utf8(out).unwrap(),
|
|
"reindexed: 42 logs, 1,208 records, 7 notes\n"
|
|
);
|
|
assert!(matches!(seen.recv().unwrap(), Message::Reindex(_)));
|
|
}
|
|
|
|
#[test]
|
|
fn an_admin_command_prints_the_reply_and_an_error_is_escaped() {
|
|
let d = dir("admin");
|
|
let socket = d.join("admin.sock");
|
|
let seen = fake(
|
|
&socket,
|
|
Message::MemoryAdminReply(MemoryAdminReply {
|
|
text: "undone: 2026-09-24-0007".to_string(),
|
|
}),
|
|
);
|
|
let mut out = Vec::new();
|
|
let ask = Ask::Admin(MemoryCommand::Undo, "2026-09-24-0007".to_string());
|
|
run(&socket, &ask, Duration::from_secs(5), &mut out).unwrap();
|
|
assert_eq!(String::from_utf8(out).unwrap(), "undone: 2026-09-24-0007\n");
|
|
match seen.recv().unwrap() {
|
|
Message::MemoryAdmin(m) => {
|
|
assert_eq!(
|
|
(m.command, m.arg.as_str()),
|
|
(MemoryCommand::Undo, "2026-09-24-0007")
|
|
)
|
|
}
|
|
other => panic!("{other:?}"),
|
|
}
|
|
let socket = d.join("refuses.sock");
|
|
let _seen = fake(
|
|
&socket,
|
|
Message::Error(WireError {
|
|
code: ErrorCode::Internal,
|
|
detail: "user.md has changed\u{1b}[31m".to_string(),
|
|
}),
|
|
);
|
|
let err = run(&socket, &ask, Duration::from_secs(5), &mut Vec::new()).unwrap_err();
|
|
assert!(err.starts_with("memoryd: user.md has changed"), "{err}");
|
|
assert!(!err.contains('\u{1b}'), "escaped: {err}");
|
|
let err = run(
|
|
&d.join("nobody.sock"),
|
|
&ask,
|
|
Duration::from_secs(1),
|
|
&mut Vec::new(),
|
|
)
|
|
.unwrap_err();
|
|
assert!(err.starts_with("cannot reach memoryd at "), "{err}");
|
|
}
|