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>
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
//! `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);
|
||||
}
|
||||
Reference in New Issue
Block a user