oxirs-cluster 0.3.1

Raft-backed distributed dataset for high availability and horizontal scaling
Documentation
//! Unit tests for the cluster metrics system.

#![cfg(test)]

use crate::cluster_metrics_manager::ClusterMetricsManager;
use crate::cluster_metrics_stats::{ClusterOperation, EnhancedLatencyStats};
use crate::cluster_metrics_types::{PerformanceRegression, RegressionSeverity};
use std::time::Duration;

#[tokio::test]
async fn test_cluster_metrics_manager_creation() {
    let manager = ClusterMetricsManager::new(1, 1000);
    assert!(manager.is_enabled().await);
}

#[tokio::test]
async fn test_operation_timing() {
    let manager = ClusterMetricsManager::new(1, 1000);

    let timer = manager
        .start_operation(ClusterOperation::AppendEntries)
        .await;
    tokio::time::sleep(Duration::from_millis(10)).await;
    timer.complete().await;

    let metrics = manager
        .get_operation_metrics(ClusterOperation::AppendEntries)
        .await;
    assert!(metrics.is_some());

    let metrics = metrics.unwrap();
    assert_eq!(metrics.count, 1);
    assert!(metrics.mean_micros >= 10_000.0);
}

#[tokio::test]
async fn test_multiple_operations() {
    let manager = ClusterMetricsManager::new(1, 1000);

    for _ in 0..10 {
        let timer = manager
            .start_operation(ClusterOperation::QueryExecution)
            .await;
        tokio::time::sleep(Duration::from_millis(5)).await;
        timer.complete().await;
    }

    let metrics = manager
        .get_operation_metrics(ClusterOperation::QueryExecution)
        .await
        .unwrap();

    assert_eq!(metrics.count, 10);
    assert!(metrics.std_dev_micros > 0.0);
}

#[tokio::test]
async fn test_gauge_operations() {
    let manager = ClusterMetricsManager::new(1, 1000);

    manager.set_gauge("active_connections", 5.0).await;
    assert_eq!(manager.get_gauge("active_connections").await, 5.0);

    manager.inc_gauge("active_connections").await;
    assert_eq!(manager.get_gauge("active_connections").await, 6.0);

    manager.dec_gauge("active_connections").await;
    assert_eq!(manager.get_gauge("active_connections").await, 5.0);
}

#[tokio::test]
async fn test_counter_operations() {
    let manager = ClusterMetricsManager::new(1, 1000);

    manager.inc_counter("total_requests").await;
    assert_eq!(manager.get_counter("total_requests").await, 1);

    manager.inc_counter_by("total_requests", 10).await;
    assert_eq!(manager.get_counter("total_requests").await, 11);
}

#[tokio::test]
async fn test_enhanced_latency_stats() {
    let mut stats = EnhancedLatencyStats::new(100);

    for i in 1..=100 {
        stats.record(i as f64 * 100.0);
    }

    assert_eq!(stats.count, 100);
    assert!(stats.mean() > 0.0);
    assert!(stats.std_dev() > 0.0);
    assert!(stats.percentile(50.0) > 0.0);
    assert!(stats.iqr() > 0.0);
}

#[tokio::test]
async fn test_prometheus_export() {
    let manager = ClusterMetricsManager::new(1, 1000);

    let timer = manager
        .start_operation(ClusterOperation::AppendEntries)
        .await;
    tokio::time::sleep(Duration::from_millis(5)).await;
    timer.complete().await;

    let prometheus = manager.export_prometheus().await;
    assert!(prometheus.contains("oxirs_cluster"));
    assert!(prometheus.contains("append_entries"));
}

#[tokio::test]
async fn test_baseline_establishment() {
    let manager = ClusterMetricsManager::new(1, 100);

    // Generate enough samples for baseline
    for _ in 0..50 {
        let timer = manager
            .start_operation(ClusterOperation::AppendEntries)
            .await;
        tokio::time::sleep(Duration::from_millis(1)).await;
        timer.complete().await;
    }

    let result = manager
        .establish_baseline(ClusterOperation::AppendEntries)
        .await;
    assert!(result.is_ok());
}

#[tokio::test]
async fn test_regression_detection() {
    let manager = ClusterMetricsManager::new(1, 100);

    // Establish baseline with fast operations
    for _ in 0..50 {
        let timer = manager
            .start_operation(ClusterOperation::AppendEntries)
            .await;
        tokio::time::sleep(Duration::from_millis(1)).await;
        timer.complete().await;
    }
    manager
        .establish_baseline(ClusterOperation::AppendEntries)
        .await
        .unwrap();

    // Now run slower operations to cause regression
    for _ in 0..30 {
        let timer = manager
            .start_operation(ClusterOperation::AppendEntries)
            .await;
        tokio::time::sleep(Duration::from_millis(5)).await; // 5x slower
        timer.complete().await;
    }

    let regressions = manager.detect_regressions().await;
    // Should detect regression due to significant slowdown
    // Note: Timing can be flaky in tests, so we just check that the detection ran
    let _detected = !regressions.is_empty();
}

#[tokio::test]
async fn test_benchmarks() {
    let manager = ClusterMetricsManager::new(1, 1000);

    let results = manager.run_benchmarks().await;
    assert!(!results.is_empty());

    for result in &results {
        assert!(result.mean_ns > 0.0);
        assert!(result.throughput > 0.0);
    }
}

#[tokio::test]
async fn test_report_generation() {
    let manager = ClusterMetricsManager::new(1, 1000);

    for _ in 0..5 {
        let timer = manager
            .start_operation(ClusterOperation::AppendEntries)
            .await;
        tokio::time::sleep(Duration::from_millis(1)).await;
        timer.complete().await;
    }

    let report = manager.generate_report().await;
    assert!(report.contains("Cluster Metrics Report"));
    assert!(report.contains("append_entries"));
}

#[tokio::test]
async fn test_enable_disable() {
    let manager = ClusterMetricsManager::new(1, 1000);

    assert!(manager.is_enabled().await);
    manager.disable().await;
    assert!(!manager.is_enabled().await);
    manager.enable().await;
    assert!(manager.is_enabled().await);
}

#[tokio::test]
async fn test_all_operations_coverage() {
    let operations = ClusterOperation::all();
    assert!(operations.len() > 20);

    for op in operations {
        let name = op.as_str();
        assert!(!name.is_empty());
    }
}

#[tokio::test]
async fn test_trend_calculation() {
    let mut stats = EnhancedLatencyStats::new(100);

    // Add increasing values to create positive trend
    for i in 1..=50 {
        stats.record(100.0 + i as f64 * 10.0);
    }

    let trend = stats.trend();
    assert!(trend > 0.0); // Should detect upward trend
}

#[tokio::test]
async fn test_coefficient_of_variation() {
    let mut stats = EnhancedLatencyStats::new(100);

    // Low variability
    for _ in 0..50 {
        stats.record(100.0);
    }
    let cv_low = stats.coefficient_of_variation();
    assert!(cv_low < 0.01);

    // High variability
    let mut stats2 = EnhancedLatencyStats::new(100);
    for i in 0..50 {
        stats2.record(if i % 2 == 0 { 50.0 } else { 150.0 });
    }
    let cv_high = stats2.coefficient_of_variation();
    assert!(cv_high > 0.3);
}

#[tokio::test]
async fn test_metrics_reset() {
    let manager = ClusterMetricsManager::new(1, 1000);

    let timer = manager
        .start_operation(ClusterOperation::AppendEntries)
        .await;
    timer.complete().await;

    manager.reset().await;

    let metrics = manager
        .get_operation_metrics(ClusterOperation::AppendEntries)
        .await;
    assert!(metrics.is_none());
}

#[tokio::test]
async fn test_regression_severity() {
    let regression = PerformanceRegression {
        operation: "test".to_string(),
        metric_name: "mean_latency".to_string(),
        baseline_value: 100.0,
        current_value: 250.0, // 150% increase
        change_percentage: 150.0,
        p_value: 0.01,
        t_statistic: 5.0,
        severity: RegressionSeverity::Critical,
        detection_method: "test".to_string(),
    };

    assert_eq!(regression.severity, RegressionSeverity::Critical);
}

#[tokio::test]
async fn test_benchmark_comparison() {
    let manager = ClusterMetricsManager::new(1, 1000);

    let results = manager.run_benchmarks().await;
    let history = manager.get_benchmark_history().await;

    assert_eq!(results.len(), history.len());
}

#[tokio::test]
async fn test_operation_timer_elapsed() {
    let manager = ClusterMetricsManager::new(1, 1000);

    let timer = manager
        .start_operation(ClusterOperation::AppendEntries)
        .await;
    tokio::time::sleep(Duration::from_millis(10)).await;

    let elapsed = timer.elapsed();
    assert!(elapsed.as_millis() >= 10);

    timer.complete().await;
}

#[tokio::test]
async fn test_disabled_metrics() {
    let manager = ClusterMetricsManager::new(1, 1000);
    manager.disable().await;

    let timer = manager
        .start_operation(ClusterOperation::AppendEntries)
        .await;
    timer.complete().await;

    // Should not record when disabled
    let metrics = manager
        .get_operation_metrics(ClusterOperation::AppendEntries)
        .await;
    assert!(metrics.is_none());
}