use processor::metrics::{
labels::{BackendLabel, CacheLayerLabel, OperationLabel, ResultLabel, StrategyLabel, WindowTypeLabel},
MetricsRegistry, MetricsServer, MetricsServerConfig, ProcessorMetrics,
};
use std::sync::Arc;
use tokio::time::{sleep, Duration};
#[tokio::test]
async fn test_metrics_basic_operations() {
let registry = MetricsRegistry::new();
let mut reg = registry.registry().write();
let metrics = ProcessorMetrics::new(&mut reg);
metrics.record_event_received();
metrics.record_event_received();
metrics.record_event_processed(ResultLabel::Success);
metrics.record_event_processed(ResultLabel::Error);
assert_eq!(metrics.events_received_total.get(), 2);
metrics.record_event_processing_duration(0.001, ResultLabel::Success);
metrics.record_event_processing_duration(0.005, ResultLabel::Error);
metrics.set_pipeline_lag(1.5);
metrics.set_backpressure_events(100);
assert_eq!(metrics.pipeline_lag_seconds.get(), 1.5);
assert_eq!(metrics.backpressure_events.get(), 100);
}
#[tokio::test]
async fn test_window_metrics() {
let registry = MetricsRegistry::new();
let mut reg = registry.registry().write();
let metrics = ProcessorMetrics::new(&mut reg);
metrics.record_window_created(WindowTypeLabel::Tumbling);
metrics.record_window_created(WindowTypeLabel::Sliding);
metrics.record_window_created(WindowTypeLabel::Session);
metrics.record_window_triggered(WindowTypeLabel::Tumbling);
metrics.record_window_triggered(WindowTypeLabel::Tumbling);
metrics.set_windows_active(WindowTypeLabel::Tumbling, 5);
metrics.set_windows_active(WindowTypeLabel::Sliding, 3);
metrics.record_window_size(WindowTypeLabel::Tumbling, 100.0);
metrics.record_window_size(WindowTypeLabel::Tumbling, 150.0);
metrics.record_late_event();
metrics.record_late_event();
assert_eq!(metrics.late_events_total.get(), 2);
}
#[tokio::test]
async fn test_state_backend_metrics() {
let registry = MetricsRegistry::new();
let mut reg = registry.registry().write();
let metrics = ProcessorMetrics::new(&mut reg);
metrics.record_state_operation(OperationLabel::Get, BackendLabel::Redis, 0.001);
metrics.record_state_operation(OperationLabel::Put, BackendLabel::Redis, 0.002);
metrics.record_state_operation(OperationLabel::Get, BackendLabel::InMemory, 0.0001);
metrics.record_state_cache_hit(CacheLayerLabel::L1);
metrics.record_state_cache_hit(CacheLayerLabel::L1);
metrics.record_state_cache_miss(CacheLayerLabel::L1);
metrics.record_state_cache_hit(CacheLayerLabel::L2);
metrics.set_state_size_bytes(BackendLabel::Redis, 1024 * 1024);
metrics.set_state_size_bytes(BackendLabel::InMemory, 512 * 1024);
let redis_labels = vec![("backend".to_string(), "redis".to_string())];
assert_eq!(
metrics.state_size_bytes.get_or_create(&redis_labels).get(),
1024 * 1024
);
}
#[tokio::test]
async fn test_deduplication_metrics() {
let registry = MetricsRegistry::new();
let mut reg = registry.registry().write();
let metrics = ProcessorMetrics::new(&mut reg);
metrics.record_dedup_check(false); metrics.record_dedup_check(true); metrics.record_dedup_check(false); metrics.record_dedup_check(true); metrics.record_dedup_check(true);
assert_eq!(metrics.dedup_events_checked_total.get(), 5);
assert_eq!(metrics.dedup_duplicates_found_total.get(), 3);
metrics.set_dedup_cache_size(1000);
assert_eq!(metrics.dedup_cache_size.get(), 1000);
metrics.dedup_cache_evictions_total.inc();
assert_eq!(metrics.dedup_cache_evictions_total.get(), 1);
}
#[tokio::test]
async fn test_normalization_metrics() {
let registry = MetricsRegistry::new();
let mut reg = registry.registry().write();
let metrics = ProcessorMetrics::new(&mut reg);
metrics.normalization_events_total.inc();
metrics.normalization_events_total.inc();
metrics.record_normalization_fill(StrategyLabel::Forward);
metrics.record_normalization_fill(StrategyLabel::Backward);
metrics.record_normalization_fill(StrategyLabel::Linear);
metrics.normalization_gaps_filled_total.inc();
metrics.normalization_gaps_filled_total.inc();
metrics.normalization_outliers_detected_total.inc();
assert_eq!(metrics.normalization_events_total.get(), 2);
assert_eq!(metrics.normalization_gaps_filled_total.get(), 2);
assert_eq!(metrics.normalization_outliers_detected_total.get(), 1);
}
#[tokio::test]
async fn test_watermark_metrics() {
let registry = MetricsRegistry::new();
let mut reg = registry.registry().write();
let metrics = ProcessorMetrics::new(&mut reg);
metrics.set_watermark_lag(2.5);
assert_eq!(metrics.watermark_lag_seconds.get(), 2.5);
metrics.record_watermark_update();
metrics.record_watermark_update();
assert_eq!(metrics.watermark_updates_total.get(), 2);
metrics.record_out_of_order_event();
metrics.record_out_of_order_event();
metrics.record_out_of_order_event();
assert_eq!(metrics.out_of_order_events_total.get(), 3);
}
#[tokio::test]
async fn test_metrics_encoding() {
let registry = MetricsRegistry::new();
let mut reg = registry.registry().write();
let metrics = ProcessorMetrics::new(&mut reg);
drop(reg);
metrics.record_event_received();
metrics.record_event_processed(ResultLabel::Success);
metrics.set_pipeline_lag(1.0);
let encoded = registry.encode().expect("Failed to encode metrics");
assert!(encoded.contains("processor_events_received"));
assert!(encoded.contains("processor_events_processed"));
assert!(encoded.contains("processor_pipeline_lag_seconds"));
}
#[tokio::test]
async fn test_metrics_server_config() {
let config = MetricsServerConfig::new("127.0.0.1", 19090);
assert_eq!(config.bind_address, "127.0.0.1");
assert_eq!(config.port, 19090);
let addr = config.socket_addr().expect("Invalid socket address");
assert_eq!(addr.port(), 19090);
}
#[tokio::test]
async fn test_metrics_snapshot() {
use processor::metrics::MetricsSnapshot;
let registry = MetricsRegistry::new();
let mut reg = registry.registry().write();
let metrics = ProcessorMetrics::new(&mut reg);
drop(reg);
metrics.record_event_received();
metrics.record_event_received();
metrics.record_event_processed(ResultLabel::Success);
metrics.record_dedup_check(true);
let snapshot = MetricsSnapshot::capture(&metrics);
assert_eq!(snapshot.events_received, 2);
assert_eq!(snapshot.events_processed_success, 1);
assert_eq!(snapshot.duplicates_found, 1);
}
#[tokio::test]
async fn test_concurrent_metric_updates() {
let registry = MetricsRegistry::new();
let mut reg = registry.registry().write();
let metrics = Arc::new(ProcessorMetrics::new(&mut reg));
drop(reg);
let mut handles = vec![];
for _ in 0..10 {
let metrics_clone = Arc::clone(&metrics);
let handle = tokio::spawn(async move {
for _ in 0..100 {
metrics_clone.record_event_received();
metrics_clone.record_event_processed(ResultLabel::Success);
}
});
handles.push(handle);
}
for handle in handles {
handle.await.expect("Task panicked");
}
assert_eq!(metrics.events_received_total.get(), 1000);
}
#[tokio::test]
async fn test_histogram_observations() {
let registry = MetricsRegistry::new();
let mut reg = registry.registry().write();
let metrics = ProcessorMetrics::new(&mut reg);
let durations = vec![0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0];
for duration in durations {
metrics.record_event_processing_duration(duration, ResultLabel::Success);
}
}
#[tokio::test]
async fn test_label_families() {
let registry = MetricsRegistry::new();
let mut reg = registry.registry().write();
let metrics = ProcessorMetrics::new(&mut reg);
metrics.record_event_processed(ResultLabel::Success);
metrics.record_event_processed(ResultLabel::Error);
metrics.record_event_processed(ResultLabel::Dropped);
let success_labels = vec![("result".to_string(), "success".to_string())];
let error_labels = vec![("result".to_string(), "error".to_string())];
let dropped_labels = vec![("result".to_string(), "dropped".to_string())];
assert_eq!(
metrics.events_processed_total.get_or_create(&success_labels).get(),
1
);
assert_eq!(
metrics.events_processed_total.get_or_create(&error_labels).get(),
1
);
assert_eq!(
metrics.events_processed_total.get_or_create(&dropped_labels).get(),
1
);
}
#[tokio::test]
#[ignore] async fn test_metrics_server_startup() {
let config = MetricsServerConfig::new("127.0.0.1", 19091);
let server = MetricsServer::new(config);
let handle = tokio::spawn(async move {
server.start().await
});
sleep(Duration::from_millis(500)).await;
handle.abort();
}