use std::io::Write;
use std::sync::{Arc, Mutex};
use tracing_subscriber::fmt::MakeWriter;
use tracing_subscriber::layer::SubscriberExt;
#[derive(Clone)]
struct BufferedWriter(Arc<Mutex<Vec<u8>>>);
impl<'a> MakeWriter<'a> for BufferedWriter {
type Writer = BufferedWriterGuard;
fn make_writer(&'a self) -> Self::Writer {
BufferedWriterGuard(self.0.clone())
}
}
struct BufferedWriterGuard(Arc<Mutex<Vec<u8>>>);
impl Write for BufferedWriterGuard {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let mut guard = self.0.lock().expect("poisoned test buffer");
guard.extend_from_slice(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[allow(dead_code)]
fn buffer_contains(buf: &Arc<Mutex<Vec<u8>>>, needle: &str) -> bool {
let guard = buf.lock().expect("poisoned test buffer");
std::str::from_utf8(&guard)
.map(|s| s.contains(needle))
.unwrap_or(false)
}
fn buffer_snapshot(buf: &Arc<Mutex<Vec<u8>>>) -> String {
let guard = buf.lock().expect("poisoned test buffer");
String::from_utf8_lossy(&guard).into_owned()
}
fn with_subscriber<F>(body: F) -> String
where
F: FnOnce(),
{
let buf: Arc<Mutex<Vec<u8>>> = Arc::new(Mutex::new(Vec::new()));
let writer = BufferedWriter(buf.clone());
let filter = tracing_subscriber::EnvFilter::new("ferriskey=trace");
let layer = tracing_subscriber::fmt::layer()
.with_writer(writer)
.with_ansi(false)
.with_target(true)
.with_span_events(tracing_subscriber::fmt::format::FmtSpan::CLOSE);
let dispatch = tracing_subscriber::registry().with(filter).with(layer);
tracing::subscriber::with_default(dispatch, body);
buffer_snapshot(&buf)
}
#[test]
fn events_no_op_when_no_subscriber() {
tracing::info!(target: "ferriskey", event = "client_created", "no-op");
tracing::warn!(target: "ferriskey", event = "timeout", "no-op");
tracing::debug!(target: "ferriskey", event = "moved_redirect", "no-op");
}
#[test]
fn events_captured_when_subscriber_installed() {
let output = with_subscriber(|| {
tracing::info!(target: "ferriskey", event = "client_created", "captured");
tracing::warn!(target: "ferriskey", event = "timeout", duration_ms = 42u64, "captured");
tracing::debug!(target: "ferriskey", event = "moved_redirect", "captured");
});
assert!(
output.contains("ferriskey"),
"expected target=ferriskey in output, got: {output}"
);
assert!(
output.contains("client_created"),
"expected 'client_created' event in output, got: {output}"
);
assert!(
output.contains("timeout"),
"expected 'timeout' event in output, got: {output}"
);
assert!(
output.contains("moved_redirect"),
"expected 'moved_redirect' event in output, got: {output}"
);
}
#[test]
#[allow(deprecated)]
fn telemetry_compat_stub_returns_zero() {
use ferriskey::Telemetry;
assert_eq!(Telemetry::incr_total_connections(7), 0);
assert_eq!(Telemetry::decr_total_connections(3), 0);
assert_eq!(Telemetry::incr_total_clients(2), 0);
assert_eq!(Telemetry::decr_total_clients(1), 0);
assert_eq!(Telemetry::total_connections(), 0);
assert_eq!(Telemetry::total_clients(), 0);
assert_eq!(Telemetry::total_values_compressed(), 0);
assert_eq!(Telemetry::total_values_decompressed(), 0);
assert_eq!(Telemetry::compression_skipped_count(), 0);
assert_eq!(Telemetry::subscription_out_of_sync_count(), 0);
assert_eq!(Telemetry::subscription_last_sync_timestamp(), 0);
Telemetry::reset();
}