Files
boxmaker/crates/gatewayd/tests/deliver_notice.rs
T
kyleandClaude Opus 5.5 2b3ae080b4 proto becomes bxcore, the one shared crate (M5b task 00)
Done the way the owner asked and the spike mapped: the crate and the
manifests renamed, then what the build reported fixed. Layers: 138
"proto unresolved" errors across 169 files; cargo fmt reordering
imports; six fixture paths built as strings in brokerd's and bxctl's
tests, which the compiler cannot see, and comments naming
crates/proto/; the crate-dependency check and its self-test. One more
the spike did not reach: a `\nproto =` key inside a printf format in
the self-test, which a word-boundary replacement skips because `n` and
`p` are both word characters.

make gate: gate: ok, 944 tests; no new entry in /tmp.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 01:48:38 -07:00

96 lines
3.1 KiB
Rust

//! The notice for an approval that `brokerd` did not bring: posted only when it has not arrived
//! within the wait (M4b spec, section 7). Do not edit.
#[path = "support/fake_loop.rs"]
mod fake_loop;
#[path = "support/tmp.rs"]
mod tmp;
use std::sync::Mutex;
use std::time::{Duration, Instant};
use bxcore::{SessionId, Timestamp, TurnEvent};
use fake_loop::{done, event, serve_loop};
use gatewayd::deliver::{Notice, Poster, deliver};
use gatewayd::mm::MmError;
use gatewayd::sessions::{Batch, Thread};
use tmp::TempDir;
const DM: &str = "d0000000000000000000000000";
const ROOT: &str = "r0000000000000000000000000";
const NOTICE: &str =
"waiting for approval 42: brokerd has not sent it here; answer it with bxctl approvals";
#[derive(Default)]
struct Record(Mutex<Vec<String>>);
impl Poster for Record {
fn post(&self, _: &str, _: &str, text: &str) -> Result<(), MmError> {
self.0.lock().unwrap().push(text.to_string());
Ok(())
}
}
/// A turn whose loop says approval 42 is pending, then answers; what was posted, and how long it took.
fn turn(tag: &str, posted: &dyn Fn(u64) -> bool, wait: Duration) -> (Vec<String>, Duration) {
let dir = TempDir::new(tag);
let socket = dir.path().join("loop.sock");
let expires = Timestamp::parse("2026-09-24T08:20:00.000Z").unwrap();
let _turns = serve_loop(&socket, move |_, _| {
vec![
event(TurnEvent::ApprovalPending {
approval: 42,
tool: "read_file".to_string(),
expires,
}),
done("done"),
]
});
let batch = Batch {
session: SessionId::new(&format!("mm-{ROOT}")).unwrap(),
thread: Thread {
channel: DM.to_string(),
root: ROOT.to_string(),
},
resume: false,
text: "go".to_string(),
};
let poster = Record::default();
let started = Instant::now();
deliver(&poster, &socket, &batch, &|_| {}, &Notice { posted, wait });
let took = started.elapsed();
(poster.0.into_inner().unwrap(), took)
}
#[test]
fn brought_already_no_notice_and_no_wait() {
let (posts, took) = turn("notice-there", &|id| id == 42, Duration::from_secs(5));
assert_eq!(posts, ["done"]);
assert!(took < Duration::from_secs(1), "{took:?}");
}
#[test]
fn brought_during_the_wait_no_notice() {
let started = Instant::now();
let later = move |id: u64| id == 42 && started.elapsed() > Duration::from_millis(300);
let (posts, took) = turn("notice-later", &later, Duration::from_secs(5));
assert_eq!(posts, ["done"]);
assert!(took < Duration::from_secs(2), "{took:?}");
}
#[test]
fn never_brought_the_notice_after_the_wait() {
let (posts, took) = turn("notice-never", &|_| false, Duration::from_millis(400));
assert_eq!(posts, [NOTICE, "done"]);
assert!(
took >= Duration::from_millis(400) && took < Duration::from_secs(3),
"{took:?}"
);
}
#[test]
fn another_approval_brought_does_not_count() {
let (posts, _) = turn("notice-other", &|id| id == 7, Duration::from_millis(200));
assert_eq!(posts, [NOTICE, "done"]);
}