1use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct PerformanceMetrics {
12 pub total_requests: u64,
14 pub successful_requests: u64,
16 pub failed_requests: u64,
18 pub current_rps: f64,
20 pub target_rps: f64,
22 pub avg_latency_ms: f64,
24 pub p95_latency_ms: u64,
26 pub p99_latency_ms: u64,
28 pub error_rate: f64,
30 pub endpoint_metrics: HashMap<String, EndpointMetrics>,
32 pub timestamp: DateTime<Utc>,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct EndpointMetrics {
39 pub request_count: u64,
41 pub avg_latency_ms: f64,
43 pub p95_latency_ms: u64,
45 pub p99_latency_ms: u64,
47 pub error_count: u64,
49 pub error_rate: f64,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct PerformanceSnapshot {
58 pub id: String,
60 pub timestamp: DateTime<Utc>,
62 pub metrics: PerformanceMetrics,
64 pub active_bottlenecks: Vec<String>,
66}
67
68impl PerformanceMetrics {
69 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 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 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}