mockforge_performance/
metrics.rs

1//! Performance Metrics
2//!
3//! Tracks and aggregates performance metrics during load simulation.
4
5use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// Performance metrics
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct PerformanceMetrics {
12    /// Total requests
13    pub total_requests: u64,
14    /// Successful requests
15    pub successful_requests: u64,
16    /// Failed requests
17    pub failed_requests: u64,
18    /// Current RPS
19    pub current_rps: f64,
20    /// Target RPS
21    pub target_rps: f64,
22    /// Average latency (ms)
23    pub avg_latency_ms: f64,
24    /// P95 latency (ms)
25    pub p95_latency_ms: u64,
26    /// P99 latency (ms)
27    pub p99_latency_ms: u64,
28    /// Error rate (0.0-1.0)
29    pub error_rate: f64,
30    /// Metrics by endpoint
31    pub endpoint_metrics: HashMap<String, EndpointMetrics>,
32    /// Timestamp
33    pub timestamp: DateTime<Utc>,
34}
35
36/// Endpoint-specific metrics
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct EndpointMetrics {
39    /// Request count
40    pub request_count: u64,
41    /// Average latency (ms)
42    pub avg_latency_ms: f64,
43    /// P95 latency (ms)
44    pub p95_latency_ms: u64,
45    /// P99 latency (ms)
46    pub p99_latency_ms: u64,
47    /// Error count
48    pub error_count: u64,
49    /// Error rate (0.0-1.0)
50    pub error_rate: f64,
51}
52
53/// Performance snapshot
54///
55/// A snapshot of performance metrics at a point in time.
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct PerformanceSnapshot {
58    /// Snapshot ID
59    pub id: String,
60    /// Timestamp
61    pub timestamp: DateTime<Utc>,
62    /// Metrics
63    pub metrics: PerformanceMetrics,
64    /// Active bottlenecks
65    pub active_bottlenecks: Vec<String>,
66}
67
68impl PerformanceMetrics {
69    /// Create new performance metrics
70    pub fn new() -> Self {
71        Self {
72            total_requests: 0,
73            successful_requests: 0,
74            failed_requests: 0,
75            current_rps: 0.0,
76            target_rps: 0.0,
77            avg_latency_ms: 0.0,
78            p95_latency_ms: 0,
79            p99_latency_ms: 0,
80            error_rate: 0.0,
81            endpoint_metrics: HashMap::new(),
82            timestamp: Utc::now(),
83        }
84    }
85
86    /// Update from latency statistics
87    pub fn update_from_latency_stats(
88        &mut self,
89        stats: &crate::latency::LatencyStats,
90        current_rps: f64,
91        target_rps: f64,
92    ) {
93        self.total_requests = stats.count as u64;
94        self.current_rps = current_rps;
95        self.target_rps = target_rps;
96        self.avg_latency_ms = stats.avg;
97        self.p95_latency_ms = stats.p95;
98        self.p99_latency_ms = stats.p99;
99        self.error_rate = stats.error_rate;
100        self.timestamp = Utc::now();
101    }
102}
103
104impl Default for PerformanceMetrics {
105    fn default() -> Self {
106        Self::new()
107    }
108}
109
110impl EndpointMetrics {
111    /// Create new endpoint metrics
112    pub fn new() -> Self {
113        Self {
114            request_count: 0,
115            avg_latency_ms: 0.0,
116            p95_latency_ms: 0,
117            p99_latency_ms: 0,
118            error_count: 0,
119            error_rate: 0.0,
120        }
121    }
122}
123
124impl Default for EndpointMetrics {
125    fn default() -> Self {
126        Self::new()
127    }
128}
129
130#[cfg(test)]
131mod tests {
132    use super::*;
133
134    #[test]
135    fn test_performance_metrics_new() {
136        let metrics = PerformanceMetrics::new();
137        assert_eq!(metrics.total_requests, 0);
138        assert_eq!(metrics.successful_requests, 0);
139        assert_eq!(metrics.failed_requests, 0);
140        assert_eq!(metrics.current_rps, 0.0);
141        assert_eq!(metrics.target_rps, 0.0);
142        assert_eq!(metrics.avg_latency_ms, 0.0);
143        assert_eq!(metrics.p95_latency_ms, 0);
144        assert_eq!(metrics.p99_latency_ms, 0);
145        assert_eq!(metrics.error_rate, 0.0);
146        assert!(metrics.endpoint_metrics.is_empty());
147    }
148
149    #[test]
150    fn test_performance_metrics_default() {
151        let metrics = PerformanceMetrics::default();
152        assert_eq!(metrics.total_requests, 0);
153    }
154
155    #[test]
156    fn test_performance_metrics_clone() {
157        let mut metrics = PerformanceMetrics::new();
158        metrics.total_requests = 100;
159        metrics.current_rps = 50.5;
160
161        let cloned = metrics.clone();
162        assert_eq!(cloned.total_requests, 100);
163        assert_eq!(cloned.current_rps, 50.5);
164    }
165
166    #[test]
167    fn test_performance_metrics_debug() {
168        let metrics = PerformanceMetrics::new();
169        let debug = format!("{:?}", metrics);
170        assert!(debug.contains("PerformanceMetrics"));
171    }
172
173    #[test]
174    fn test_performance_metrics_serialize() {
175        let metrics = PerformanceMetrics::new();
176        let json = serde_json::to_string(&metrics).unwrap();
177        assert!(json.contains("\"total_requests\":0"));
178        assert!(json.contains("\"current_rps\":0.0"));
179    }
180
181    #[test]
182    fn test_performance_metrics_deserialize() {
183        let json = r#"{
184            "total_requests": 100,
185            "successful_requests": 95,
186            "failed_requests": 5,
187            "current_rps": 10.5,
188            "target_rps": 15.0,
189            "avg_latency_ms": 50.0,
190            "p95_latency_ms": 100,
191            "p99_latency_ms": 150,
192            "error_rate": 0.05,
193            "endpoint_metrics": {},
194            "timestamp": "2024-01-01T00:00:00Z"
195        }"#;
196
197        let metrics: PerformanceMetrics = serde_json::from_str(json).unwrap();
198        assert_eq!(metrics.total_requests, 100);
199        assert_eq!(metrics.successful_requests, 95);
200        assert_eq!(metrics.failed_requests, 5);
201        assert_eq!(metrics.current_rps, 10.5);
202        assert_eq!(metrics.target_rps, 15.0);
203    }
204
205    #[test]
206    fn test_performance_metrics_update_from_latency_stats() {
207        let mut metrics = PerformanceMetrics::new();
208        let stats = crate::latency::LatencyStats {
209            count: 100,
210            min: 10,
211            max: 200,
212            avg: 50.0,
213            median: 45.0,
214            p95: 150,
215            p99: 180,
216            error_rate: 0.1,
217        };
218
219        metrics.update_from_latency_stats(&stats, 25.0, 30.0);
220
221        assert_eq!(metrics.total_requests, 100);
222        assert_eq!(metrics.current_rps, 25.0);
223        assert_eq!(metrics.target_rps, 30.0);
224        assert_eq!(metrics.avg_latency_ms, 50.0);
225        assert_eq!(metrics.p95_latency_ms, 150);
226        assert_eq!(metrics.p99_latency_ms, 180);
227        assert_eq!(metrics.error_rate, 0.1);
228    }
229
230    #[test]
231    fn test_endpoint_metrics_new() {
232        let metrics = EndpointMetrics::new();
233        assert_eq!(metrics.request_count, 0);
234        assert_eq!(metrics.avg_latency_ms, 0.0);
235        assert_eq!(metrics.p95_latency_ms, 0);
236        assert_eq!(metrics.p99_latency_ms, 0);
237        assert_eq!(metrics.error_count, 0);
238        assert_eq!(metrics.error_rate, 0.0);
239    }
240
241    #[test]
242    fn test_endpoint_metrics_default() {
243        let metrics = EndpointMetrics::default();
244        assert_eq!(metrics.request_count, 0);
245    }
246
247    #[test]
248    fn test_endpoint_metrics_clone() {
249        let mut metrics = EndpointMetrics::new();
250        metrics.request_count = 50;
251        metrics.avg_latency_ms = 25.5;
252
253        let cloned = metrics.clone();
254        assert_eq!(cloned.request_count, 50);
255        assert_eq!(cloned.avg_latency_ms, 25.5);
256    }
257
258    #[test]
259    fn test_endpoint_metrics_debug() {
260        let metrics = EndpointMetrics::new();
261        let debug = format!("{:?}", metrics);
262        assert!(debug.contains("EndpointMetrics"));
263    }
264
265    #[test]
266    fn test_endpoint_metrics_serialize() {
267        let metrics = EndpointMetrics::new();
268        let json = serde_json::to_string(&metrics).unwrap();
269        assert!(json.contains("\"request_count\":0"));
270        assert!(json.contains("\"error_rate\":0.0"));
271    }
272
273    #[test]
274    fn test_performance_snapshot_clone() {
275        let snapshot = PerformanceSnapshot {
276            id: "test-id".to_string(),
277            timestamp: Utc::now(),
278            metrics: PerformanceMetrics::new(),
279            active_bottlenecks: vec!["Network".to_string()],
280        };
281
282        let cloned = snapshot.clone();
283        assert_eq!(cloned.id, "test-id");
284        assert_eq!(cloned.active_bottlenecks.len(), 1);
285    }
286
287    #[test]
288    fn test_performance_snapshot_debug() {
289        let snapshot = PerformanceSnapshot {
290            id: "test-snapshot".to_string(),
291            timestamp: Utc::now(),
292            metrics: PerformanceMetrics::new(),
293            active_bottlenecks: vec![],
294        };
295
296        let debug = format!("{:?}", snapshot);
297        assert!(debug.contains("PerformanceSnapshot"));
298        assert!(debug.contains("test-snapshot"));
299    }
300
301    #[test]
302    fn test_performance_snapshot_serialize() {
303        let snapshot = PerformanceSnapshot {
304            id: "snap-1".to_string(),
305            timestamp: Utc::now(),
306            metrics: PerformanceMetrics::new(),
307            active_bottlenecks: vec!["Cpu".to_string(), "Memory".to_string()],
308        };
309
310        let json = serde_json::to_string(&snapshot).unwrap();
311        assert!(json.contains("\"id\":\"snap-1\""));
312        assert!(json.contains("Cpu"));
313        assert!(json.contains("Memory"));
314    }
315
316    #[test]
317    fn test_performance_metrics_with_endpoint_metrics() {
318        let mut metrics = PerformanceMetrics::new();
319
320        let mut endpoint_metrics = EndpointMetrics::new();
321        endpoint_metrics.request_count = 50;
322        endpoint_metrics.avg_latency_ms = 100.0;
323
324        metrics.endpoint_metrics.insert("/api/users".to_string(), endpoint_metrics);
325
326        assert_eq!(metrics.endpoint_metrics.len(), 1);
327        assert!(metrics.endpoint_metrics.contains_key("/api/users"));
328        assert_eq!(metrics.endpoint_metrics.get("/api/users").unwrap().request_count, 50);
329    }
330}