Files
fips/src/testutil.rs
T
Johnathan Corgan ab8a9b4867 Carry the discovery request correlator into the sub-floor path MTU warning
Every sibling warning in the lookup response handler logs the request id, and
this one could not: the coordinate-caching action did not carry it and the
response is out of scope by the time the action is driven. An operator reading
the refusal had a counter and no way to tie it to an exchange.

Thread the id through the action, and assert it on the log line rather than on
the action alone. Mutating either hop, the value the shell passes or the field
on the warning, leaves the rest of the suite green, so the log line is the only
place the pairing can be pinned.

The tracing capture the config tests already had moves into the shared test
helpers rather than being copied, and grows an async-scoped variant, since an
async test cannot wrap its awaits in a closure.
2026-08-15 13:50:17 +00:00

76 lines
2.7 KiB
Rust

//! Crate-wide generic test helpers.
use crate::NodeAddr;
/// Build a `NodeAddr` from a single discriminating byte in position 0.
pub(crate) fn make_node_addr(val: u8) -> NodeAddr {
let mut bytes = [0u8; 16];
bytes[0] = val;
NodeAddr::from_bytes(bytes)
}
/// Collects emitted tracing events so a test can assert on a log line.
///
/// Some behaviour is reported only in the log: a structured field an operator
/// greps on is part of the contract even when no counter or return value
/// carries it. Installed with `tracing::subscriber::with_default`, which is
/// thread-local, so tests running in parallel do not see each other's events.
#[derive(Clone, Default)]
pub(crate) struct LogCapture(std::sync::Arc<std::sync::Mutex<Vec<String>>>);
impl LogCapture {
/// Only the captured lines emitted at WARN.
pub(crate) fn warnings(&self) -> Vec<String> {
self.0
.lock()
.unwrap()
.iter()
.filter(|line| line.starts_with("WARN"))
.cloned()
.collect()
}
}
impl<S: tracing::Subscriber> tracing_subscriber::Layer<S> for LogCapture {
fn on_event(
&self,
event: &tracing::Event<'_>,
_ctx: tracing_subscriber::layer::Context<'_, S>,
) {
struct Fields(String);
impl tracing::field::Visit for Fields {
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
self.0.push_str(&format!(" {}={:?}", field.name(), value));
}
}
let mut fields = Fields(event.metadata().level().to_string());
event.record(&mut fields);
self.0.lock().unwrap().push(fields.0);
}
}
/// Run `f` with a capturing subscriber installed, returning its value and the capture.
pub(crate) fn capture_logs<T>(f: impl FnOnce() -> T) -> (T, LogCapture) {
use tracing_subscriber::layer::SubscriberExt;
let capture = LogCapture::default();
let subscriber = tracing_subscriber::registry().with(capture.clone());
let out = tracing::subscriber::with_default(subscriber, f);
(out, capture)
}
/// Install a capturing subscriber for the rest of the current scope.
///
/// The async counterpart of [`capture_logs`]: an `async` test cannot wrap its
/// awaits in a closure, so it holds this guard instead and reads the capture
/// once the awaited work has run.
pub(crate) fn capture_logs_scoped() -> (LogCapture, tracing::subscriber::DefaultGuard) {
use tracing_subscriber::layer::SubscriberExt;
let capture = LogCapture::default();
let subscriber = tracing_subscriber::registry().with(capture.clone());
let guard = tracing::subscriber::set_default(subscriber);
(capture, guard)
}