//! Validation metrics collection and performance monitoring //! //! This module provides comprehensive metrics collection for validation operations //! including performance metrics, success rates, error tracking, and system //! resource utilization. use std::collections::{HashMap, VecDeque}; use std::time::Duration; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; /// Main validation metrics collector #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ValidationMetrics { /// Performance metrics pub performance: PerformanceMetrics, /// Success and failure rates pub success_rates: SuccessRateMetrics, /// Error tracking metrics pub error_metrics: ErrorMetrics, /// Resource utilization metrics pub resource_metrics: ResourceMetrics, /// Throughput metrics pub throughput_metrics: ThroughputMetrics, /// Quality metrics pub quality_metrics: QualityMetrics, /// Historical metrics (last N measurements) pub historical: HistoricalMetrics, /// Metrics metadata pub metadata: MetricsMetadata, } /// Performance-related metrics #[derive(Debug, Clone, Serialize, Deserialize)] pub struct PerformanceMetrics { /// Total validation duration in milliseconds pub validation_duration_ms: u64, /// Average validation time per record pub avg_validation_time_ms: f64, /// 50th percentile validation time pub p50_validation_time_ms: f64, /// 95th percentile validation time pub p95_validation_time_ms: f64, /// 99th percentile validation time pub p99_validation_time_ms: f64, /// Maximum validation time observed pub max_validation_time_ms: u64, /// Minimum validation time observed pub min_validation_time_ms: u64, /// Number of validation rules applied pub rule_count: usize, /// Number of records processed pub records_processed: usize, /// Validation queue depth pub queue_depth: usize, /// Active validation count pub active_validations: usize, } /// Success and failure rate metrics #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SuccessRateMetrics { /// Total number of validations pub total_validations: usize, /// Number of successful validations pub successful_validations: usize, /// Number of failed validations pub failed_validations: usize, /// Overall success rate (0.0 - 1.0) pub success_rate: f64, /// Success rate over last hour pub hourly_success_rate: f64, /// Success rate over last day pub daily_success_rate: f64, /// Success rate trend (increasing/decreasing) pub success_rate_trend: TrendDirection, /// First validation timestamp pub first_validation: Option>, /// Last validation timestamp pub last_validation: Option>, } /// Error tracking metrics #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ErrorMetrics { /// Total number of errors pub total_errors: usize, /// Errors by type pub errors_by_type: HashMap, /// Errors by severity pub errors_by_severity: HashMap, /// Recent error rate (errors per minute) pub error_rate_per_minute: f64, /// Error frequency over time pub error_frequency: Vec, /// Most common error types pub top_error_types: Vec<(String, usize)>, /// Error resolution time statistics pub error_resolution_stats: ErrorResolutionStats, } /// Resource utilization metrics #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ResourceMetrics { /// CPU utilization percentage pub cpu_utilization: f64, /// Memory usage in bytes pub memory_usage_bytes: usize, /// Peak memory usage pub peak_memory_bytes: usize, /// Memory utilization percentage pub memory_utilization: f64, /// I/O operations count pub io_operations: usize, /// Network I/O statistics pub network_io: NetworkIoStats, /// Disk I/O statistics pub disk_io: DiskIoStats, /// Thread pool utilization pub thread_utilization: ThreadUtilization, } /// Throughput metrics #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ThroughputMetrics { /// Records processed per second pub records_per_second: f64, /// Peak throughput achieved pub peak_records_per_second: f64, /// Average throughput over last hour pub hourly_avg_throughput: f64, /// Throughput trend pub throughput_trend: TrendDirection, /// Throughput percentiles pub throughput_percentiles: ThroughputPercentiles, /// Batch processing statistics pub batch_stats: BatchStats, /// Streaming processing statistics pub streaming_stats: StreamingStats, } /// Data quality metrics #[derive(Debug, Clone, Serialize, Deserialize)] pub struct QualityMetrics { /// Average data quality score pub avg_quality_score: f64, /// Quality score distribution pub quality_distribution: QualityDistribution, /// Quality trend over time pub quality_trend: TrendDirection, /// Quality metrics by dimension pub quality_by_dimension: HashMap, /// Anomaly detection statistics pub anomaly_stats: AnomalyStats, /// Schema compliance statistics pub schema_compliance: SchemaComplianceStats, } /// Historical metrics storage #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HistoricalMetrics { /// Maximum number of historical points to keep pub max_history_points: usize, /// Historical validation times pub validation_times: VecDeque, /// Historical success rates pub success_rates: VecDeque, /// Historical throughput measurements pub throughput_history: VecDeque, /// Historical resource usage pub resource_history: VecDeque, /// Historical quality scores pub quality_history: VecDeque, } /// Metadata about metrics collection #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MetricsMetadata { /// When metrics collection started pub collection_started: DateTime, /// Last metrics update timestamp pub last_updated: DateTime, /// Metrics collection interval (seconds) pub collection_interval_seconds: u64, /// Metrics version pub version: String, /// Collector configuration pub config: MetricsConfig, } /// Configuration for metrics collection #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MetricsConfig { /// Enable detailed metrics collection pub detailed_collection: bool, /// Maximum history retention pub max_history_days: u32, /// Enable resource monitoring pub monitor_resources: bool, /// Enable quality tracking pub track_quality: bool, /// Metrics aggregation interval pub aggregation_interval_seconds: u64, } /// Trend direction enumeration #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub enum TrendDirection { /// Increasing trend Increasing, /// Decreasing trend Decreasing, /// Stable trend Stable, /// Volatile/unpredictable trend Volatile, /// Unknown/insufficient data Unknown, } /// Error frequency point #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ErrorFrequencyPoint { /// Timestamp pub timestamp: DateTime, /// Error count pub count: usize, /// Error types at this point pub error_types: Vec, } /// Error resolution statistics #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ErrorResolutionStats { /// Average time to resolve errors (minutes) pub avg_resolution_time_minutes: f64, /// Number of unresolved errors pub unresolved_errors: usize, /// Error escalation rate pub escalation_rate: f64, /// Recovery success rate pub recovery_success_rate: f64, } /// Network I/O statistics #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NetworkIoStats { /// Bytes sent pub bytes_sent: usize, /// Bytes received pub bytes_received: usize, /// Packets sent pub packets_sent: usize, /// Packets received pub packets_received: usize, /// Network latency (milliseconds) pub latency_ms: f64, } /// Disk I/O statistics #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DiskIoStats { /// Bytes read from disk pub bytes_read: usize, /// Bytes written to disk pub bytes_written: usize, /// Read operations count pub read_operations: usize, /// Write operations count pub write_operations: usize, /// Average I/O wait time pub avg_io_wait_ms: f64, } /// Thread pool utilization #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ThreadUtilization { /// Active threads count pub active_threads: usize, /// Total thread pool size pub total_threads: usize, /// Thread utilization percentage pub utilization_percentage: f64, /// Average task queue length pub avg_queue_length: f64, } /// Throughput percentiles #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ThroughputPercentiles { /// 50th percentile pub p50: f64, /// 75th percentile pub p75: f64, /// 90th percentile pub p90: f64, /// 95th percentile pub p95: f64, /// 99th percentile pub p99: f64, } /// Batch processing statistics #[derive(Debug, Clone, Serialize, Deserialize)] pub struct BatchStats { /// Total batches processed pub total_batches: usize, /// Average batch size pub avg_batch_size: f64, /// Average batch processing time pub avg_batch_time_ms: f64, /// Batch success rate pub batch_success_rate: f64, } /// Streaming processing statistics #[derive(Debug, Clone, Serialize, Deserialize)] pub struct StreamingStats { /// Current stream throughput pub current_throughput: f64, /// Stream backlog size pub backlog_size: usize, /// Stream latency (milliseconds) pub stream_latency_ms: f64, /// Buffer utilization pub buffer_utilization: f64, } /// Quality score distribution #[derive(Debug, Clone, Serialize, Deserialize)] pub struct QualityDistribution { /// Excellent quality (>90%) pub excellent_count: usize, /// Good quality (70-90%) pub good_count: usize, /// Fair quality (50-70%) pub fair_count: usize, /// Poor quality (<50%) pub poor_count: usize, } /// Anomaly detection statistics #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AnomalyStats { /// Total anomalies detected pub total_anomalies: usize, /// Anomalies by type pub anomalies_by_type: HashMap, /// Anomaly detection rate pub detection_rate: f64, /// False positive rate pub false_positive_rate: f64, } /// Schema compliance statistics #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SchemaComplianceStats { /// Overall compliance rate pub compliance_rate: f64, /// Schema violations count pub violations_count: usize, /// Compliance by field pub field_compliance: HashMap, } /// Success rate measurement point #[derive(Debug, Clone, Serialize, Deserialize)] pub struct SuccessRatePoint { /// Measurement timestamp pub timestamp: DateTime, /// Success rate at this point pub rate: f64, /// Sample size pub sample_size: usize, } /// Throughput measurement point #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ThroughputPoint { /// Measurement timestamp pub timestamp: DateTime, /// Throughput value pub throughput: f64, /// Measurement duration pub duration_seconds: f64, } /// Resource usage measurement point #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ResourceUsagePoint { /// Measurement timestamp pub timestamp: DateTime, /// CPU usage percentage pub cpu_percent: f64, /// Memory usage in bytes pub memory_bytes: usize, /// I/O operations count pub io_ops: usize, } /// Quality measurement point #[derive(Debug, Clone, Serialize, Deserialize)] pub struct QualityPoint { /// Measurement timestamp pub timestamp: DateTime, /// Quality score pub score: f64, /// Number of records in sample pub sample_size: usize, } impl Default for ValidationMetrics { fn default() -> Self { Self::new() } } impl ValidationMetrics { /// Create a new metrics collector pub fn new() -> Self { let now = Utc::now(); Self { performance: PerformanceMetrics::default(), success_rates: SuccessRateMetrics::default(), error_metrics: ErrorMetrics::default(), resource_metrics: ResourceMetrics::default(), throughput_metrics: ThroughputMetrics::default(), quality_metrics: QualityMetrics::default(), historical: HistoricalMetrics::new(1000), // Keep last 1000 points metadata: MetricsMetadata { collection_started: now, last_updated: now, collection_interval_seconds: 60, version: "1.0.0".to_string(), config: MetricsConfig::default(), }, } } /// Record a validation operation pub fn record_validation(&mut self, duration: Duration, success: bool) { let duration_ms = duration.as_millis() as f64; // Update performance metrics self.performance.records_processed += 1; self.performance.validation_duration_ms += duration.as_millis() as u64; // Update running averages let total_records = self.performance.records_processed as f64; let prev_avg = self.performance.avg_validation_time_ms; self.performance.avg_validation_time_ms = prev_avg + (duration_ms - prev_avg) / total_records; // Update min/max if self.performance.records_processed == 1 { self.performance.min_validation_time_ms = duration.as_millis() as u64; self.performance.max_validation_time_ms = duration.as_millis() as u64; } else { self.performance.min_validation_time_ms = self .performance .min_validation_time_ms .min(duration.as_millis() as u64); self.performance.max_validation_time_ms = self .performance .max_validation_time_ms .max(duration.as_millis() as u64); } // Update success rates self.success_rates.total_validations += 1; if success { self.success_rates.successful_validations += 1; } else { self.success_rates.failed_validations += 1; } self.success_rates.success_rate = self.success_rates.successful_validations as f64 / self.success_rates.total_validations as f64; // Update timestamps let now = Utc::now(); if self.success_rates.first_validation.is_none() { self.success_rates.first_validation = Some(now); } self.success_rates.last_validation = Some(now); // Update historical data if self.historical.validation_times.len() >= self.historical.max_history_points { self.historical.validation_times.pop_front(); } self.historical.validation_times.push_back(duration_ms); // Update metadata self.metadata.last_updated = now; // Calculate percentiles if we have enough data if self.historical.validation_times.len() >= 10 { self.calculate_percentiles(); } } /// Record an error pub fn record_error(&mut self, error_type: &str, severity: &str) { self.error_metrics.total_errors += 1; *self .error_metrics .errors_by_type .entry(error_type.to_string()) .or_insert(0) += 1; *self .error_metrics .errors_by_severity .entry(severity.to_string()) .or_insert(0) += 1; // Update error frequency let now = Utc::now(); self.error_metrics .error_frequency .push(ErrorFrequencyPoint { timestamp: now, count: 1, error_types: vec![error_type.to_string()], }); // Keep only recent error frequency data (last 1000 points) if self.error_metrics.error_frequency.len() > 1000 { self.error_metrics.error_frequency.remove(0); } // Calculate error rate (simplified) let recent_errors = self .error_metrics .error_frequency .iter() .filter(|e| (now - e.timestamp).num_minutes() <= 1) .map(|e| e.count) .sum::(); self.error_metrics.error_rate_per_minute = recent_errors as f64; // Update top error types let mut error_types: Vec<_> = self .error_metrics .errors_by_type .iter() .map(|(k, v)| (k.clone(), *v)) .collect(); error_types.sort_by(|a, b| b.1.cmp(&a.1)); self.error_metrics.top_error_types = error_types.into_iter().take(10).collect(); } /// Update resource metrics pub fn update_resource_metrics( &mut self, cpu_percent: f64, memory_bytes: usize, io_ops: usize, ) { self.resource_metrics.cpu_utilization = cpu_percent; self.resource_metrics.memory_usage_bytes = memory_bytes; self.resource_metrics.peak_memory_bytes = self.resource_metrics.peak_memory_bytes.max(memory_bytes); self.resource_metrics.io_operations += io_ops; // Add to historical data let point = ResourceUsagePoint { timestamp: Utc::now(), cpu_percent, memory_bytes, io_ops, }; if self.historical.resource_history.len() >= self.historical.max_history_points { self.historical.resource_history.pop_front(); } self.historical.resource_history.push_back(point); } /// Calculate throughput metrics pub fn calculate_throughput(&mut self, time_window_seconds: f64) { if time_window_seconds <= 0.0 || self.performance.records_processed == 0 { return; } let records_per_second = self.performance.records_processed as f64 / time_window_seconds; self.throughput_metrics.records_per_second = records_per_second; self.throughput_metrics.peak_records_per_second = self .throughput_metrics .peak_records_per_second .max(records_per_second); // Add throughput point to history let point = ThroughputPoint { timestamp: Utc::now(), throughput: records_per_second, duration_seconds: time_window_seconds, }; if self.historical.throughput_history.len() >= self.historical.max_history_points { self.historical.throughput_history.pop_front(); } self.historical.throughput_history.push_back(point); // Calculate trend (simplified) if self.historical.throughput_history.len() >= 5 { let recent: Vec = self .historical .throughput_history .iter() .rev() .take(5) .map(|p| p.throughput) .collect(); self.throughput_metrics.throughput_trend = self.calculate_trend(&recent); } } /// Update quality metrics pub fn update_quality_metrics(&mut self, quality_score: f64, sample_size: usize) { // Update running average let prev_total = self .quality_metrics .quality_by_dimension .values() .sum::(); let count = self.quality_metrics.quality_by_dimension.len() as f64; if count == 0.0 { self.quality_metrics.avg_quality_score = quality_score; } else { self.quality_metrics.avg_quality_score = (prev_total + quality_score) / (count + 1.0); } // Update quality distribution match quality_score { s if s >= 0.9 => self.quality_metrics.quality_distribution.excellent_count += 1, s if s >= 0.7 => self.quality_metrics.quality_distribution.good_count += 1, s if s >= 0.5 => self.quality_metrics.quality_distribution.fair_count += 1, _ => self.quality_metrics.quality_distribution.poor_count += 1, } // Add to historical data let point = QualityPoint { timestamp: Utc::now(), score: quality_score, sample_size, }; if self.historical.quality_history.len() >= self.historical.max_history_points { self.historical.quality_history.pop_front(); } self.historical.quality_history.push_back(point); } /// Get current metrics summary pub fn summary(&self) -> MetricsSummary { MetricsSummary { total_validations: self.success_rates.total_validations, success_rate: self.success_rates.success_rate, avg_validation_time_ms: self.performance.avg_validation_time_ms, current_throughput: self.throughput_metrics.records_per_second, error_rate: self.error_metrics.error_rate_per_minute, avg_quality_score: self.quality_metrics.avg_quality_score, cpu_utilization: self.resource_metrics.cpu_utilization, memory_usage_mb: self.resource_metrics.memory_usage_bytes as f64 / 1_000_000.0, last_updated: self.metadata.last_updated, } } /// Reset all metrics pub fn reset(&mut self) { *self = Self::new(); } /// Export metrics to JSON pub fn to_json(&self) -> Result { serde_json::to_string_pretty(self) } fn calculate_percentiles(&mut self) { let mut times: Vec = self.historical.validation_times.iter().copied().collect(); times.sort_by(|a, b| a.total_cmp(b)); if !times.is_empty() { let len = times.len(); self.performance.p50_validation_time_ms = times[len * 50 / 100]; self.performance.p95_validation_time_ms = times[len * 95 / 100]; self.performance.p99_validation_time_ms = times[len * 99 / 100]; } } fn calculate_trend(&self, values: &[f64]) -> TrendDirection { if values.len() < 3 { return TrendDirection::Unknown; } // Simple trend calculation based on linear regression slope let n = values.len() as f64; let sum_x = (0..values.len()).sum::() as f64; let sum_y = values.iter().sum::(); let sum_xy = values .iter() .enumerate() .map(|(i, &y)| i as f64 * y) .sum::(); let sum_x2 = (0..values.len()).map(|i| (i * i) as f64).sum::(); let slope = (n * sum_xy - sum_x * sum_y) / (n * sum_x2 - sum_x * sum_x); match slope { s if s > 0.1 => TrendDirection::Increasing, s if s < -0.1 => TrendDirection::Decreasing, s if s.abs() <= 0.1 => TrendDirection::Stable, _ => TrendDirection::Volatile, } } } /// Condensed metrics summary #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MetricsSummary { /// Total validations performed pub total_validations: usize, /// Overall success rate pub success_rate: f64, /// Average validation time pub avg_validation_time_ms: f64, /// Current throughput pub current_throughput: f64, /// Current error rate pub error_rate: f64, /// Average quality score pub avg_quality_score: f64, /// CPU utilization pub cpu_utilization: f64, /// Memory usage in MB pub memory_usage_mb: f64, /// Last update timestamp pub last_updated: DateTime, } impl Default for PerformanceMetrics { fn default() -> Self { Self { validation_duration_ms: 0, avg_validation_time_ms: 0.0, p50_validation_time_ms: 0.0, p95_validation_time_ms: 0.0, p99_validation_time_ms: 0.0, max_validation_time_ms: 0, min_validation_time_ms: 0, rule_count: 0, records_processed: 0, queue_depth: 0, active_validations: 0, } } } impl Default for SuccessRateMetrics { fn default() -> Self { Self { total_validations: 0, successful_validations: 0, failed_validations: 0, success_rate: 0.0, hourly_success_rate: 0.0, daily_success_rate: 0.0, success_rate_trend: TrendDirection::Unknown, first_validation: None, last_validation: None, } } } impl Default for ErrorMetrics { fn default() -> Self { Self { total_errors: 0, errors_by_type: HashMap::new(), errors_by_severity: HashMap::new(), error_rate_per_minute: 0.0, error_frequency: Vec::new(), top_error_types: Vec::new(), error_resolution_stats: ErrorResolutionStats { avg_resolution_time_minutes: 0.0, unresolved_errors: 0, escalation_rate: 0.0, recovery_success_rate: 0.0, }, } } } impl Default for ResourceMetrics { fn default() -> Self { Self { cpu_utilization: 0.0, memory_usage_bytes: 0, peak_memory_bytes: 0, memory_utilization: 0.0, io_operations: 0, network_io: NetworkIoStats { bytes_sent: 0, bytes_received: 0, packets_sent: 0, packets_received: 0, latency_ms: 0.0, }, disk_io: DiskIoStats { bytes_read: 0, bytes_written: 0, read_operations: 0, write_operations: 0, avg_io_wait_ms: 0.0, }, thread_utilization: ThreadUtilization { active_threads: 0, total_threads: 0, utilization_percentage: 0.0, avg_queue_length: 0.0, }, } } } impl Default for ThroughputMetrics { fn default() -> Self { Self { records_per_second: 0.0, peak_records_per_second: 0.0, hourly_avg_throughput: 0.0, throughput_trend: TrendDirection::Unknown, throughput_percentiles: ThroughputPercentiles { p50: 0.0, p75: 0.0, p90: 0.0, p95: 0.0, p99: 0.0, }, batch_stats: BatchStats { total_batches: 0, avg_batch_size: 0.0, avg_batch_time_ms: 0.0, batch_success_rate: 0.0, }, streaming_stats: StreamingStats { current_throughput: 0.0, backlog_size: 0, stream_latency_ms: 0.0, buffer_utilization: 0.0, }, } } } impl Default for QualityMetrics { fn default() -> Self { Self { avg_quality_score: 0.0, quality_distribution: QualityDistribution { excellent_count: 0, good_count: 0, fair_count: 0, poor_count: 0, }, quality_trend: TrendDirection::Unknown, quality_by_dimension: HashMap::new(), anomaly_stats: AnomalyStats { total_anomalies: 0, anomalies_by_type: HashMap::new(), detection_rate: 0.0, false_positive_rate: 0.0, }, schema_compliance: SchemaComplianceStats { compliance_rate: 0.0, violations_count: 0, field_compliance: HashMap::new(), }, } } } impl Default for MetricsConfig { fn default() -> Self { Self { detailed_collection: true, max_history_days: 30, monitor_resources: true, track_quality: true, aggregation_interval_seconds: 60, } } } impl HistoricalMetrics { fn new(max_points: usize) -> Self { Self { max_history_points: max_points, validation_times: VecDeque::new(), success_rates: VecDeque::new(), throughput_history: VecDeque::new(), resource_history: VecDeque::new(), quality_history: VecDeque::new(), } } } #[cfg(test)] mod tests { use super::*; use std::time::Duration; #[test] fn test_metrics_creation() { let metrics = ValidationMetrics::new(); assert_eq!(metrics.success_rates.total_validations, 0); assert_eq!(metrics.performance.records_processed, 0); } #[test] fn test_record_validation() { let mut metrics = ValidationMetrics::new(); // Record successful validation metrics.record_validation(Duration::from_millis(100), true); assert_eq!(metrics.success_rates.total_validations, 1); assert_eq!(metrics.success_rates.successful_validations, 1); assert_eq!(metrics.success_rates.success_rate, 1.0); assert_eq!(metrics.performance.avg_validation_time_ms, 100.0); } #[test] fn test_record_error() { let mut metrics = ValidationMetrics::new(); metrics.record_error("validation_error", "critical"); assert_eq!(metrics.error_metrics.total_errors, 1); assert_eq!( metrics.error_metrics.errors_by_type.get("validation_error"), Some(&1) ); assert_eq!( metrics.error_metrics.errors_by_severity.get("critical"), Some(&1) ); } #[test] fn test_success_rate_calculation() { let mut metrics = ValidationMetrics::new(); // Record mixed results metrics.record_validation(Duration::from_millis(50), true); metrics.record_validation(Duration::from_millis(75), false); metrics.record_validation(Duration::from_millis(25), true); assert_eq!(metrics.success_rates.total_validations, 3); assert_eq!(metrics.success_rates.successful_validations, 2); assert_eq!(metrics.success_rates.failed_validations, 1); assert_eq!(metrics.success_rates.success_rate, 2.0 / 3.0); } #[test] fn test_throughput_calculation() { let mut metrics = ValidationMetrics::new(); // Record some validations metrics.record_validation(Duration::from_millis(100), true); metrics.record_validation(Duration::from_millis(150), true); // Calculate throughput over 2 seconds metrics.calculate_throughput(2.0); assert_eq!(metrics.throughput_metrics.records_per_second, 1.0); } #[test] fn test_quality_metrics_update() { let mut metrics = ValidationMetrics::new(); metrics.update_quality_metrics(0.85, 100); assert_eq!(metrics.quality_metrics.avg_quality_score, 0.85); assert_eq!(metrics.quality_metrics.quality_distribution.good_count, 1); } #[test] fn test_trend_direction() { let metrics = ValidationMetrics::new(); // Test increasing trend let increasing = vec![1.0, 2.0, 3.0, 4.0, 5.0]; assert_eq!( metrics.calculate_trend(&increasing), TrendDirection::Increasing ); // Test decreasing trend let decreasing = vec![5.0, 4.0, 3.0, 2.0, 1.0]; assert_eq!( metrics.calculate_trend(&decreasing), TrendDirection::Decreasing ); // Test stable trend let stable = vec![3.0, 3.0, 3.0, 3.0, 3.0]; assert_eq!(metrics.calculate_trend(&stable), TrendDirection::Stable); } #[test] fn test_metrics_summary() { let mut metrics = ValidationMetrics::new(); metrics.record_validation(Duration::from_millis(100), true); metrics.update_quality_metrics(0.9, 50); let summary = metrics.summary(); assert_eq!(summary.total_validations, 1); assert_eq!(summary.success_rate, 1.0); assert_eq!(summary.avg_quality_score, 0.9); } #[test] fn test_metrics_reset() { let mut metrics = ValidationMetrics::new(); metrics.record_validation(Duration::from_millis(100), true); metrics.record_error("test_error", "warning"); assert_eq!(metrics.success_rates.total_validations, 1); assert_eq!(metrics.error_metrics.total_errors, 1); metrics.reset(); assert_eq!(metrics.success_rates.total_validations, 0); assert_eq!(metrics.error_metrics.total_errors, 0); } }