#[test]
fn capture_logs_captures_warn_with_fields() {
let captured = super::capture_logs(|| {
tracing::warn!(bind = "0.0.0.0:1", public_routes = 1_u64, "gate warned");
});
assert!(
captured.contains("0.0.0.0:1"),
"field value must appear in the capture: {captured}"
);
assert!(
captured.contains("public_routes=1"),
"numeric field must appear as key=value: {captured}"
);
assert!(
captured.to_lowercase().contains("warn"),
"level must be rendered: {captured}"
);
assert!(
captured.contains("gate warned"),
"message must appear in the capture: {captured}"
);
}
#[test]
fn capture_logs_empty_when_closure_is_silent() {
let captured = super::capture_logs(|| ());
assert!(
captured.is_empty(),
"silent closure must produce no output: {captured}"
);
}
#[tokio::test]
async fn capture_logs_async_passes_output_and_captures() {
let (out, captured) = super::capture_logs_async(async {
tracing::warn!("async side fired");
7_u8
})
.await;
assert_eq!(out, 7);
assert!(
captured.contains("async side fired"),
"event inside the future must be captured: {captured}"
);
}
#[tokio::test]
async fn capture_logs_async_silent_future_yields_empty_capture() {
let (out, captured) = super::capture_logs_async(async { 1_u8 }).await;
assert!(
captured.is_empty(),
"silent future must produce no output: {captured}"
);
assert_eq!(out, 1);
}
#[test]
fn capture_warns_returns_output_and_captures_field_pairs() {
let (out, warns) = super::capture_warns(|| {
tracing::warn!(path = "/srv", "disk quota low");
3_i32
});
assert_eq!(out, 3);
assert!(
!warns.is_empty(),
"the event must be captured: {} entries — {warns:?}",
warns.len()
);
let first = &warns[0];
assert!(
first.contains("message=disk quota low"),
"message must render as message=text: {first}"
);
assert!(
first.contains("path=\"/srv\""),
"field must render as field=\"value\": {first}"
);
}
#[test]
fn capture_warns_two_events_preserve_emission_order() {
let (_, warns) = super::capture_warns(|| {
tracing::warn!("first");
tracing::warn!("second");
});
assert!(
warns.len() >= 2,
"both events must be captured: {} entries — {warns:?}",
warns.len()
);
let first_pos = warns
.iter()
.position(|entry| entry.contains("first"))
.expect("first event must be captured");
let second_pos = warns
.iter()
.position(|entry| entry.contains("second"))
.expect("second event must be captured");
assert!(
first_pos < second_pos,
"emission order must be preserved: first at {first_pos}, second at {second_pos} — {warns:?}"
);
}
#[test]
fn capture_warns_captures_warn_and_error_not_lower_levels() {
let (_, warns) = super::capture_warns(|| {
tracing::error!("probe error");
tracing::warn!("probe warn");
tracing::info!("probe info");
tracing::debug!("probe debug");
tracing::trace!("probe trace");
});
assert_eq!(
warns.len(),
2,
"exactly the error and warn events must be captured: {warns:?}"
);
let error_pos = warns
.iter()
.position(|entry| entry.contains("probe error"))
.expect("error event must be captured");
let warn_pos = warns
.iter()
.position(|entry| entry.contains("probe warn"))
.expect("warn event must be captured");
let info_hit = warns.iter().any(|entry| entry.contains("probe info"));
let debug_hit = warns.iter().any(|entry| entry.contains("probe debug"));
let trace_hit = warns.iter().any(|entry| entry.contains("probe trace"));
assert!(!info_hit, "info events must not be captured: {warns:?}");
assert!(!debug_hit, "debug events must not be captured: {warns:?}");
assert!(!trace_hit, "trace events must not be captured: {warns:?}");
assert!(
error_pos < warn_pos,
"emission order must be preserved: error at {error_pos}, warn at {warn_pos} — {warns:?}"
);
}
#[test]
fn capture_debugs_captures_debug_and_more_severe_events() {
let ((), events, span_records) = super::capture_debugs_with_span_records(|| {
tracing::debug!("probe debug");
tracing::warn!("probe warn");
let span = tracing::info_span!("probe_span", attempt = tracing::field::Empty);
let _e = span.enter();
tracing::Span::current().record("attempt", 2_i64);
});
assert!(
events.iter().any(|entry| entry.contains("probe debug")),
"debug event must be captured: {events:?}"
);
assert!(
events.iter().any(|entry| entry.contains("probe warn")),
"warn event must be captured: {events:?}"
);
assert!(
span_records.iter().any(|entry| entry.contains("attempt=2")),
"span record must be captured: {span_records:?}"
);
}
#[test]
fn captured_field_ends_at_next_field_boundary() {
assert_eq!(
super::captured_field("k=\"1\" next=\"2 3\"", "k"),
Some("\"1\"")
);
assert_eq!(
super::captured_field("k=\"1\" next=\"2 3\"", "next"),
Some("\"2 3\"")
);
assert_eq!(super::captured_field("k=\"1\"", "missing"), None);
}
#[test]
fn record_field_finds_message_part_then_field() {
let lines = vec!["attempt=2 message=\"send failed\"".to_string()];
assert_eq!(
super::record_field(&lines, "send failed", "attempt"),
Some("2")
);
assert_eq!(super::record_field(&lines, "absent text", "attempt"), None);
}