Files
boxmaker/crates/brokerd/tests/serve_accept_break.rs
T
kyleandClaude Opus 5.5 cfa02479c1 Test --accept-break on a real break through the brokerd binary
A plain start refuses, --accept-break records the break and serves, and the
next plain start serves. The given tests covered this only at library level.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-22 21:52:54 -07:00

112 lines
3.7 KiB
Rust

//! `brokerd serve --accept-break` on a real break, through the binary: it records the break and
//! serves, and the next plain start serves too (spec section 12, "Audit edges"; missing from the
//! given tests, found in the M3a review).
#[path = "support/tmp.rs"]
mod tmp;
use std::io::Read;
use std::os::unix::net::UnixStream;
use std::path::Path;
use std::process::{Command, Stdio};
use std::time::{Duration, Instant};
use brokerd::audit::verify_dir;
use tmp::TempDir;
/// Start `brokerd serve` with `extra`, wait until it listens, stop it, and return its stderr.
fn serve_once(config: &Path, tools: &Path, extra: &[&str]) -> String {
let mut child = Command::new(env!("CARGO_BIN_EXE_brokerd"))
.args(["serve", "--config"])
.arg(config)
.args(extra)
.stdout(Stdio::null())
.stderr(Stdio::piped())
.spawn()
.unwrap();
let until = Instant::now() + Duration::from_secs(10);
while UnixStream::connect(tools).is_err() {
if let Some(status) = child.try_wait().unwrap() {
let mut err = String::new();
child
.stderr
.take()
.unwrap()
.read_to_string(&mut err)
.unwrap();
panic!("brokerd exited with {status}: {err}");
}
assert!(Instant::now() < until, "brokerd never listened");
std::thread::sleep(Duration::from_millis(20));
}
child.kill().unwrap();
child.wait().unwrap();
let mut err = String::new();
child
.stderr
.take()
.unwrap()
.read_to_string(&mut err)
.unwrap();
err
}
#[test]
fn a_break_is_accepted_and_the_next_plain_start_serves() {
let dir = TempDir::new("accept-break");
let audit = dir.path().join("audit");
std::fs::create_dir_all(&audit).unwrap();
std::fs::create_dir_all(dir.path().join("grants")).unwrap();
let fixture =
Path::new(env!("CARGO_MANIFEST_DIR")).join("../proto/tests/fixtures/audit/changed-byte");
// The damage is in the first file; an ordinary start checks only the latest, so copy that one
// alone, as `audit_startup.rs` does.
std::fs::copy(
fixture.join("2026-09-17.jsonl"),
audit.join("2026-09-17.jsonl"),
)
.unwrap();
let config = dir.write(
"brokerd.toml",
&format!(
"[paths]\nhome = \"{0}\"\ngrants = \"{0}/grants\"\n",
dir.path().display()
),
);
let tools = dir.path().join("run/loop-broker/broker.sock");
// A plain start must refuse: wait for it to exit, and fail (not hang) if it serves instead.
let mut plain = Command::new(env!("CARGO_BIN_EXE_brokerd"))
.args(["serve", "--config"])
.arg(&config)
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()
.unwrap();
let until = Instant::now() + Duration::from_secs(10);
let status = loop {
if let Some(status) = plain.try_wait().unwrap() {
break status;
}
if Instant::now() > until {
plain.kill().unwrap();
panic!("a plain start served on a broken chain");
}
std::thread::sleep(Duration::from_millis(20));
};
assert_eq!(status.code(), Some(1), "a plain start refuses first");
let printed = serve_once(&config, &tools, &["--accept-break"]);
assert!(
printed.contains("audit: accepted the break at"),
"{printed}"
);
let printed = serve_once(&config, &tools, &[]);
assert!(!printed.contains("accepted the break"), "{printed}");
let report = verify_dir(&audit).unwrap();
assert_eq!(report.failure, None, "{:?}", report.failure);
assert_eq!(report.accepted_breaks.len(), 1);
}