1use std::collections::HashMap;
6
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct ApiResponse<T> {
16 pub success: bool,
17 pub data: Option<T>,
18 pub error: Option<String>,
19}
20
21#[derive(Debug, Clone, Deserialize)]
25pub struct RoutesWrapper {
26 #[serde(default)]
27 pub routes: Vec<RouteInfo>,
28}
29
30#[derive(Debug, Clone, Deserialize)]
31pub struct PluginsWrapper {
32 #[serde(default)]
33 pub plugins: Vec<PluginInfo>,
34 #[serde(default)]
35 pub total: u64,
36}
37
38#[derive(Debug, Clone, Deserialize)]
39pub struct ContractDiffWrapper {
40 #[serde(default)]
41 pub captures: Vec<ContractDiffCapture>,
42}
43
44#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct DashboardData {
48 pub server_info: ServerInfo,
49 pub system_info: DashboardSystemInfo,
50 pub metrics: SimpleMetrics,
51 pub servers: Vec<ServerStatus>,
52 pub recent_logs: Vec<RequestLog>,
53 pub system: SystemInfo,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct ServerInfo {
58 #[serde(default)]
59 pub version: String,
60 #[serde(default)]
61 pub build_time: String,
62 #[serde(default)]
63 pub git_sha: String,
64 pub http_server: Option<String>,
65 pub ws_server: Option<String>,
66 pub grpc_server: Option<String>,
67 pub graphql_server: Option<String>,
68 #[serde(default)]
69 pub api_enabled: bool,
70 #[serde(default)]
71 pub admin_port: u16,
72}
73
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct DashboardSystemInfo {
76 #[serde(default)]
77 pub os: String,
78 #[serde(default)]
79 pub arch: String,
80 #[serde(default)]
81 pub uptime: u64,
82 #[serde(default)]
83 pub memory_usage: u64,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct SimpleMetrics {
88 #[serde(default)]
89 pub total_requests: u64,
90 #[serde(default)]
91 pub active_requests: u64,
92 #[serde(default)]
93 pub average_response_time: f64,
94 #[serde(default)]
95 pub error_rate: f64,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct ServerStatus {
100 pub server_type: String,
101 pub address: Option<String>,
102 #[serde(default)]
103 pub running: bool,
104 pub start_time: Option<DateTime<Utc>>,
105 pub uptime_seconds: Option<u64>,
106 #[serde(default)]
107 pub active_connections: u64,
108 #[serde(default)]
109 pub total_requests: u64,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
113pub struct SystemInfo {
114 #[serde(default)]
115 pub version: String,
116 #[serde(default)]
117 pub uptime_seconds: u64,
118 #[serde(default)]
119 pub memory_usage_mb: u64,
120 #[serde(default)]
121 pub cpu_usage_percent: f64,
122 #[serde(default)]
123 pub active_threads: usize,
124 #[serde(default)]
125 pub total_routes: usize,
126 #[serde(default)]
127 pub total_fixtures: usize,
128 #[serde(default)]
129 pub peak_memory_usage_mb: u64,
130 #[serde(default)]
131 pub peak_cpu_usage_percent: f64,
132 #[serde(default)]
133 pub peak_error_rate: f64,
134 #[serde(default)]
135 pub peaks_since: Option<DateTime<Utc>>,
136 #[serde(default)]
139 pub tps: f64,
140 #[serde(default)]
141 pub peak_tps: f64,
142 #[serde(default)]
144 pub rps_200: f64,
145 #[serde(default)]
146 pub peak_rps_200: f64,
147 #[serde(default)]
150 pub cps: f64,
151 #[serde(default)]
152 pub peak_cps: f64,
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct RequestLog {
159 pub id: String,
160 pub timestamp: DateTime<Utc>,
161 pub method: String,
162 pub path: String,
163 pub status_code: u16,
164 #[serde(default)]
165 pub response_time_ms: u64,
166 pub client_ip: Option<String>,
167 pub user_agent: Option<String>,
168 #[serde(default)]
169 pub headers: HashMap<String, String>,
170 #[serde(default)]
171 pub response_size_bytes: u64,
172 pub error_message: Option<String>,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct RouteInfo {
179 pub method: Option<String>,
180 pub path: String,
181 #[serde(default)]
182 pub priority: i32,
183 #[serde(default)]
184 pub has_fixtures: bool,
185 pub latency_ms: Option<u64>,
186 #[serde(default)]
187 pub request_count: u64,
188 pub last_request: Option<DateTime<Utc>>,
189 #[serde(default)]
190 pub error_count: u64,
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct MetricsData {
197 #[serde(default)]
198 pub requests_by_endpoint: HashMap<String, u64>,
199 #[serde(default)]
200 pub response_time_percentiles: HashMap<String, u64>,
201 pub endpoint_percentiles: Option<HashMap<String, HashMap<String, u64>>>,
202 pub latency_over_time: Option<Vec<(DateTime<Utc>, u64)>>,
203 #[serde(default)]
204 pub error_rate_by_endpoint: HashMap<String, f64>,
205 #[serde(default)]
206 pub memory_usage_over_time: Vec<(DateTime<Utc>, u64)>,
207 #[serde(default)]
208 pub cpu_usage_over_time: Vec<(DateTime<Utc>, f64)>,
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
214pub struct HealthCheck {
215 pub status: String,
216 #[serde(default)]
217 pub services: HashMap<String, String>,
218 pub last_check: Option<DateTime<Utc>>,
219 #[serde(default)]
220 pub issues: Vec<String>,
221}
222
223#[derive(Debug, Clone, Serialize, Deserialize)]
224pub struct HealthProbe {
225 pub status: String,
226 #[serde(default)]
227 pub checks: HashMap<String, serde_json::Value>,
228}
229
230#[derive(Debug, Clone, Default, Serialize, Deserialize)]
233pub struct ConfigState {
234 #[serde(default)]
235 pub latency: LatencyConfig,
236 #[serde(default)]
237 pub faults: FaultConfig,
238 #[serde(default)]
239 pub proxy: ProxyConfig,
240 #[serde(default)]
241 pub traffic_shaping: TrafficShapingConfig,
242 #[serde(default)]
243 pub validation: ValidationConfig,
244}
245
246#[derive(Debug, Clone, Default, Serialize, Deserialize)]
247pub struct LatencyConfig {
248 #[serde(default)]
249 pub enabled: bool,
250 #[serde(default)]
251 pub base_ms: u64,
252 #[serde(default)]
253 pub jitter_ms: u64,
254 #[serde(default)]
255 pub tag_overrides: HashMap<String, u64>,
256}
257
258#[derive(Debug, Clone, Default, Serialize, Deserialize)]
259pub struct FaultConfig {
260 #[serde(default)]
261 pub enabled: bool,
262 #[serde(default)]
263 pub failure_rate: f64,
264 #[serde(default)]
265 pub status_codes: Vec<u16>,
266}
267
268#[derive(Debug, Clone, Default, Serialize, Deserialize)]
269pub struct ProxyConfig {
270 #[serde(default)]
271 pub enabled: bool,
272 pub upstream_url: Option<String>,
273 #[serde(default)]
274 pub timeout_seconds: u64,
275}
276
277#[derive(Debug, Clone, Default, Serialize, Deserialize)]
278pub struct TrafficShapingConfig {
279 #[serde(default)]
280 pub enabled: bool,
281 #[serde(default)]
282 pub bandwidth: serde_json::Value,
283 #[serde(default)]
284 pub burst_loss: serde_json::Value,
285}
286
287#[derive(Debug, Clone, Default, Serialize, Deserialize)]
288pub struct ValidationConfig {
289 #[serde(default)]
290 pub mode: String,
291 #[serde(default)]
292 pub aggregate_errors: bool,
293 #[serde(default)]
294 pub validate_responses: bool,
295 #[serde(default)]
296 pub overrides: serde_json::Value,
297}
298
299#[derive(Debug, Clone, Serialize, Deserialize)]
302pub struct PluginInfo {
303 pub id: String,
304 pub name: String,
305 #[serde(default)]
306 pub version: String,
307 #[serde(default)]
308 pub types: Vec<String>,
309 #[serde(default)]
310 pub status: String,
311 #[serde(default)]
312 pub healthy: bool,
313 #[serde(default)]
314 pub description: String,
315 #[serde(default)]
316 pub author: String,
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize)]
322pub struct FixtureInfo {
323 pub id: String,
324 #[serde(default)]
325 pub protocol: String,
326 #[serde(default)]
327 pub method: String,
328 #[serde(default)]
329 pub path: String,
330 pub saved_at: Option<DateTime<Utc>>,
331 #[serde(default)]
332 pub file_size: u64,
333 #[serde(default)]
334 pub file_path: String,
335 #[serde(default)]
336 pub fingerprint: String,
337 #[serde(default)]
338 pub metadata: serde_json::Value,
339}
340
341#[derive(Debug, Clone, Serialize, Deserialize)]
344pub struct SmokeTestResult {
345 pub id: String,
346 #[serde(default)]
347 pub name: String,
348 #[serde(default)]
349 pub method: String,
350 #[serde(default)]
351 pub path: String,
352 #[serde(default)]
353 pub description: String,
354 pub last_run: Option<DateTime<Utc>>,
355 #[serde(default)]
356 pub status: String,
357 pub response_time_ms: Option<u64>,
358 pub error_message: Option<String>,
359 pub status_code: Option<u16>,
360 pub duration_seconds: Option<f64>,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize)]
366pub struct WorkspaceInfo {
367 pub id: String,
368 #[serde(default)]
369 pub name: String,
370 #[serde(default)]
371 pub description: String,
372 #[serde(default)]
373 pub active: bool,
374 pub created_at: Option<DateTime<Utc>>,
375 #[serde(default)]
376 pub environments: Vec<String>,
377}
378
379#[derive(Debug, Clone, Serialize, Deserialize)]
382pub struct ChaosStatus {
383 #[serde(default)]
384 pub enabled: bool,
385 pub active_scenario: Option<String>,
386 #[serde(default)]
387 pub settings: serde_json::Value,
388}
389
390#[derive(Debug, Clone, Serialize, Deserialize)]
393pub struct TimeTravelStatus {
394 #[serde(default)]
395 pub enabled: bool,
396 pub current_time: Option<DateTime<Utc>>,
397 #[serde(default, alias = "time_scale")]
399 pub scale_factor: Option<f64>,
400 pub real_time: Option<DateTime<Utc>>,
401 #[serde(default)]
402 pub scheduled_responses: u64,
403}
404
405#[derive(Debug, Clone, Serialize, Deserialize)]
408pub struct ChainInfo {
409 pub id: String,
410 #[serde(default)]
411 pub name: String,
412 #[serde(default)]
413 pub steps: Vec<serde_json::Value>,
414 #[serde(default)]
415 pub description: String,
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize)]
421pub struct AuditEntry {
422 pub id: String,
423 pub timestamp: Option<DateTime<Utc>>,
424 #[serde(default)]
425 pub action: String,
426 #[serde(default)]
427 pub user: String,
428 #[serde(default)]
429 pub details: serde_json::Value,
430}
431
432#[derive(Debug, Clone, Serialize, Deserialize)]
435pub struct AnalyticsSummary {
436 #[serde(default, alias = "total_requests")]
438 pub request_rate: f64,
439 #[serde(default)]
440 pub unique_endpoints: u64,
441 #[serde(default, alias = "error_rate")]
443 pub error_rate_percent: f64,
444 #[serde(default, alias = "avg_response_time")]
445 pub p95_latency_ms: f64,
446 #[serde(default)]
447 pub active_connections: f64,
448 #[serde(default)]
449 pub top_endpoints: Vec<EndpointStat>,
450 #[serde(default)]
452 pub timestamp: Option<String>,
453}
454
455#[derive(Debug, Clone, Serialize, Deserialize)]
456pub struct EndpointStat {
457 #[serde(default)]
458 pub endpoint: String,
459 #[serde(default)]
460 pub count: u64,
461 #[serde(default)]
462 pub avg_time: f64,
463}
464
465#[derive(Debug, Clone, Serialize, Deserialize)]
468pub struct RecorderStatus {
469 #[serde(default)]
470 pub recording: bool,
471 #[serde(default)]
472 pub recorded_count: u64,
473}
474
475#[derive(Debug, Clone, Serialize, Deserialize)]
478pub struct VerificationResult {
479 #[serde(default)]
480 pub matched: bool,
481 #[serde(default)]
482 pub count: u64,
483 #[serde(default)]
484 pub details: serde_json::Value,
485}
486
487#[derive(Debug, Clone, Serialize, Deserialize)]
490pub struct WorldStateEntry {
491 #[serde(default)]
492 pub key: String,
493 #[serde(default)]
494 pub value: serde_json::Value,
495 pub updated_at: Option<DateTime<Utc>>,
496}
497
498#[derive(Debug, Clone, Serialize, Deserialize)]
501pub struct FederationPeer {
502 #[serde(default)]
503 pub id: String,
504 #[serde(default)]
505 pub url: String,
506 #[serde(default)]
507 pub status: String,
508 pub last_sync: Option<DateTime<Utc>>,
509}
510
511#[derive(Debug, Clone, Serialize, Deserialize)]
514pub struct ContractDiffCapture {
515 pub id: String,
516 #[serde(default)]
517 pub path: String,
518 #[serde(default)]
519 pub method: String,
520 #[serde(default)]
521 pub diff_status: String,
522 pub captured_at: Option<DateTime<Utc>>,
523 #[serde(default)]
525 pub analyzed: bool,
526 #[serde(default)]
527 pub source: String,
528 #[serde(default)]
529 pub headers: HashMap<String, String>,
530 #[serde(default)]
531 pub query_params: HashMap<String, String>,
532}
533
534#[cfg(test)]
535mod tests {
536 use super::*;
537
538 #[test]
539 fn deserialize_api_response_success() {
540 let json = r#"{
541 "success": true,
542 "data": "hello",
543 "error": null
544 }"#;
545 let resp: ApiResponse<String> = serde_json::from_str(json).unwrap();
546 assert!(resp.success);
547 assert_eq!(resp.data.unwrap(), "hello");
548 assert!(resp.error.is_none());
549 }
550
551 #[test]
552 fn deserialize_api_response_with_timestamp() {
553 let json = r#"{
555 "success": true,
556 "data": "hello",
557 "error": null,
558 "timestamp": "2026-03-07T05:16:29.407667010Z"
559 }"#;
560 let resp: ApiResponse<String> = serde_json::from_str(json).unwrap();
561 assert!(resp.success);
562 assert_eq!(resp.data.unwrap(), "hello");
563 }
564
565 #[test]
566 fn deserialize_api_response_error() {
567 let json = r#"{
568 "success": false,
569 "data": null,
570 "error": "something went wrong"
571 }"#;
572 let resp: ApiResponse<String> = serde_json::from_str(json).unwrap();
573 assert!(!resp.success);
574 assert!(resp.data.is_none());
575 assert_eq!(resp.error.unwrap(), "something went wrong");
576 }
577
578 #[test]
579 fn deserialize_server_info_minimal() {
580 let json = r#"{
581 "version": "0.3.31"
582 }"#;
583 let info: ServerInfo = serde_json::from_str(json).unwrap();
584 assert_eq!(info.version, "0.3.31");
585 assert!(info.build_time.is_empty());
586 assert!(info.git_sha.is_empty());
587 assert!(info.http_server.is_none());
588 assert!(!info.api_enabled);
589 assert_eq!(info.admin_port, 0);
590 }
591
592 #[test]
593 fn deserialize_server_info_full() {
594 let json = r#"{
595 "version": "0.3.31",
596 "build_time": "2025-01-15T10:00:00Z",
597 "git_sha": "abc123",
598 "http_server": "http://0.0.0.0:3000",
599 "ws_server": "ws://0.0.0.0:3001",
600 "grpc_server": null,
601 "graphql_server": null,
602 "api_enabled": true,
603 "admin_port": 9080
604 }"#;
605 let info: ServerInfo = serde_json::from_str(json).unwrap();
606 assert_eq!(info.version, "0.3.31");
607 assert_eq!(info.http_server.unwrap(), "http://0.0.0.0:3000");
608 assert!(info.api_enabled);
609 assert_eq!(info.admin_port, 9080);
610 }
611
612 #[test]
613 fn deserialize_request_log() {
614 let json = r#"{
615 "id": "req-001",
616 "timestamp": "2025-06-15T12:00:00Z",
617 "method": "GET",
618 "path": "/api/users",
619 "status_code": 200,
620 "response_time_ms": 42,
621 "client_ip": "127.0.0.1",
622 "user_agent": "curl/8.0",
623 "headers": {"content-type": "application/json"},
624 "response_size_bytes": 1024,
625 "error_message": null
626 }"#;
627 let log: RequestLog = serde_json::from_str(json).unwrap();
628 assert_eq!(log.id, "req-001");
629 assert_eq!(log.method, "GET");
630 assert_eq!(log.path, "/api/users");
631 assert_eq!(log.status_code, 200);
632 assert_eq!(log.response_time_ms, 42);
633 assert_eq!(log.client_ip.unwrap(), "127.0.0.1");
634 assert_eq!(log.headers.get("content-type").unwrap(), "application/json");
635 }
636
637 #[test]
638 fn deserialize_request_log_minimal() {
639 let json = r#"{
640 "id": "req-002",
641 "timestamp": "2025-06-15T12:00:00Z",
642 "method": "POST",
643 "path": "/api/data",
644 "status_code": 500
645 }"#;
646 let log: RequestLog = serde_json::from_str(json).unwrap();
647 assert_eq!(log.id, "req-002");
648 assert_eq!(log.status_code, 500);
649 assert_eq!(log.response_time_ms, 0);
650 assert!(log.client_ip.is_none());
651 assert!(log.headers.is_empty());
652 }
653
654 #[test]
655 fn deserialize_route_info() {
656 let json = r#"{
657 "method": "GET",
658 "path": "/api/users/{id}",
659 "priority": 10,
660 "has_fixtures": true,
661 "latency_ms": 50,
662 "request_count": 100,
663 "last_request": "2025-06-15T12:30:00Z",
664 "error_count": 2
665 }"#;
666 let route: RouteInfo = serde_json::from_str(json).unwrap();
667 assert_eq!(route.method.unwrap(), "GET");
668 assert_eq!(route.path, "/api/users/{id}");
669 assert_eq!(route.priority, 10);
670 assert!(route.has_fixtures);
671 assert_eq!(route.latency_ms.unwrap(), 50);
672 assert_eq!(route.request_count, 100);
673 assert_eq!(route.error_count, 2);
674 }
675
676 #[test]
677 fn deserialize_route_info_minimal() {
678 let json = r#"{
679 "path": "/health"
680 }"#;
681 let route: RouteInfo = serde_json::from_str(json).unwrap();
682 assert!(route.method.is_none());
683 assert_eq!(route.path, "/health");
684 assert_eq!(route.priority, 0);
685 assert!(!route.has_fixtures);
686 assert!(route.latency_ms.is_none());
687 }
688
689 #[test]
690 fn deserialize_health_check() {
691 let json = r#"{
692 "status": "healthy",
693 "services": {"http": "up", "grpc": "up"},
694 "last_check": "2025-06-15T12:00:00Z",
695 "issues": []
696 }"#;
697 let health: HealthCheck = serde_json::from_str(json).unwrap();
698 assert_eq!(health.status, "healthy");
699 assert_eq!(health.services.len(), 2);
700 assert!(health.issues.is_empty());
701 }
702
703 #[test]
704 fn deserialize_health_check_with_issues() {
705 let json = r#"{
706 "status": "degraded",
707 "issues": ["kafka disconnected", "high latency"]
708 }"#;
709 let health: HealthCheck = serde_json::from_str(json).unwrap();
710 assert_eq!(health.status, "degraded");
711 assert_eq!(health.issues.len(), 2);
712 assert_eq!(health.issues[0], "kafka disconnected");
713 }
714
715 #[test]
716 fn deserialize_plugin_info() {
717 let json = r#"{
718 "id": "plugin-001",
719 "name": "response-graphql",
720 "version": "1.0.0",
721 "types": ["response", "graphql"],
722 "status": "active",
723 "healthy": true,
724 "description": "GraphQL response plugin",
725 "author": "MockForge"
726 }"#;
727 let plugin: PluginInfo = serde_json::from_str(json).unwrap();
728 assert_eq!(plugin.id, "plugin-001");
729 assert_eq!(plugin.name, "response-graphql");
730 assert!(plugin.healthy);
731 assert_eq!(plugin.types.len(), 2);
732 }
733
734 #[test]
735 fn deserialize_config_state() {
736 let json = r#"{
737 "latency": {
738 "enabled": true,
739 "base_ms": 100,
740 "jitter_ms": 20,
741 "tag_overrides": {"fast": 10}
742 },
743 "faults": {
744 "enabled": false,
745 "failure_rate": 0.05,
746 "status_codes": [500, 503]
747 },
748 "proxy": {
749 "enabled": false,
750 "upstream_url": null,
751 "timeout_seconds": 30
752 },
753 "traffic_shaping": {
754 "enabled": false,
755 "bandwidth": {},
756 "burst_loss": {}
757 },
758 "validation": {
759 "mode": "strict",
760 "aggregate_errors": true,
761 "validate_responses": false,
762 "overrides": {}
763 }
764 }"#;
765 let config: ConfigState = serde_json::from_str(json).unwrap();
766 assert!(config.latency.enabled);
767 assert_eq!(config.latency.base_ms, 100);
768 assert_eq!(config.latency.jitter_ms, 20);
769 assert!(!config.faults.enabled);
770 assert_eq!(config.faults.status_codes, vec![500, 503]);
771 assert!(!config.proxy.enabled);
772 assert_eq!(config.validation.mode, "strict");
773 }
774
775 #[test]
776 fn deserialize_fixture_info() {
777 let json = r#"{
778 "id": "fix-001",
779 "protocol": "http",
780 "method": "GET",
781 "path": "/api/users",
782 "saved_at": "2025-06-15T10:00:00Z",
783 "file_size": 2048,
784 "file_path": "/fixtures/users.json",
785 "fingerprint": "abc123def",
786 "metadata": {"tag": "v1"}
787 }"#;
788 let fixture: FixtureInfo = serde_json::from_str(json).unwrap();
789 assert_eq!(fixture.id, "fix-001");
790 assert_eq!(fixture.protocol, "http");
791 assert_eq!(fixture.file_size, 2048);
792 }
793
794 #[test]
795 fn deserialize_smoke_test_result() {
796 let json = r#"{
797 "id": "smoke-001",
798 "name": "Health endpoint",
799 "method": "GET",
800 "path": "/health",
801 "description": "Verify health endpoint returns 200",
802 "last_run": "2025-06-15T12:00:00Z",
803 "status": "passed",
804 "response_time_ms": 15,
805 "status_code": 200
806 }"#;
807 let result: SmokeTestResult = serde_json::from_str(json).unwrap();
808 assert_eq!(result.id, "smoke-001");
809 assert_eq!(result.status, "passed");
810 assert_eq!(result.status_code.unwrap(), 200);
811 }
812
813 #[test]
814 fn deserialize_workspace_info() {
815 let json = r#"{
816 "id": "ws-001",
817 "name": "default",
818 "description": "Default workspace",
819 "active": true,
820 "created_at": "2025-01-01T00:00:00Z",
821 "environments": ["dev", "staging"]
822 }"#;
823 let ws: WorkspaceInfo = serde_json::from_str(json).unwrap();
824 assert_eq!(ws.id, "ws-001");
825 assert!(ws.active);
826 assert_eq!(ws.environments, vec!["dev", "staging"]);
827 }
828
829 #[test]
830 fn deserialize_chaos_status() {
831 let json = r#"{
832 "enabled": true,
833 "active_scenario": "network-partition",
834 "settings": {"probability": 0.1}
835 }"#;
836 let chaos: ChaosStatus = serde_json::from_str(json).unwrap();
837 assert!(chaos.enabled);
838 assert_eq!(chaos.active_scenario.unwrap(), "network-partition");
839 }
840
841 #[test]
842 fn deserialize_time_travel_status() {
843 let json = r#"{
844 "enabled": true,
845 "current_time": "2025-01-01T00:00:00Z",
846 "scale_factor": 2.0,
847 "scheduled_responses": 5
848 }"#;
849 let tt: TimeTravelStatus = serde_json::from_str(json).unwrap();
850 assert!(tt.enabled);
851 assert!((tt.scale_factor.unwrap() - 2.0).abs() < f64::EPSILON);
852 assert_eq!(tt.scheduled_responses, 5);
853 }
854
855 #[test]
856 fn deserialize_time_travel_status_with_alias() {
857 let json = r#"{
859 "enabled": false,
860 "time_scale": 1.5
861 }"#;
862 let tt: TimeTravelStatus = serde_json::from_str(json).unwrap();
863 assert!(!tt.enabled);
864 assert!((tt.scale_factor.unwrap() - 1.5).abs() < f64::EPSILON);
865 }
866
867 #[test]
868 fn deserialize_chain_info() {
869 let json = r#"{
870 "id": "chain-001",
871 "name": "User flow",
872 "steps": [{"action": "create"}, {"action": "read"}],
873 "description": "End-to-end user CRUD"
874 }"#;
875 let chain: ChainInfo = serde_json::from_str(json).unwrap();
876 assert_eq!(chain.id, "chain-001");
877 assert_eq!(chain.steps.len(), 2);
878 }
879
880 #[test]
881 fn deserialize_audit_entry() {
882 let json = r#"{
883 "id": "audit-001",
884 "timestamp": "2025-06-15T12:00:00Z",
885 "action": "config.update",
886 "user": "admin",
887 "details": {"field": "latency_ms", "old": 50, "new": 100}
888 }"#;
889 let entry: AuditEntry = serde_json::from_str(json).unwrap();
890 assert_eq!(entry.id, "audit-001");
891 assert_eq!(entry.action, "config.update");
892 assert_eq!(entry.user, "admin");
893 }
894
895 #[test]
896 fn deserialize_analytics_summary() {
897 let json = r#"{
899 "request_rate": 42.5,
900 "p95_latency_ms": 15.3,
901 "error_rate_percent": 0.5,
902 "active_connections": 12.0
903 }"#;
904 let summary: AnalyticsSummary = serde_json::from_str(json).unwrap();
905 assert!((summary.request_rate - 42.5).abs() < f64::EPSILON);
906 assert!((summary.error_rate_percent - 0.5).abs() < f64::EPSILON);
907 }
908
909 #[test]
910 fn deserialize_analytics_summary_with_aliases() {
911 let json = r#"{
913 "total_requests": 10000,
914 "unique_endpoints": 25,
915 "error_rate": 0.02,
916 "avg_response_time": 45.5,
917 "top_endpoints": [
918 {"endpoint": "/api/users", "count": 5000, "avg_time": 30.0}
919 ]
920 }"#;
921 let summary: AnalyticsSummary = serde_json::from_str(json).unwrap();
922 assert!((summary.request_rate - 10000.0).abs() < f64::EPSILON);
923 assert_eq!(summary.unique_endpoints, 25);
924 assert_eq!(summary.top_endpoints.len(), 1);
925 }
926
927 #[test]
928 fn deserialize_recorder_status() {
929 let json = r#"{
930 "recording": true,
931 "recorded_count": 42
932 }"#;
933 let recorder: RecorderStatus = serde_json::from_str(json).unwrap();
934 assert!(recorder.recording);
935 assert_eq!(recorder.recorded_count, 42);
936 }
937
938 #[test]
939 fn deserialize_verification_result() {
940 let json = r#"{
941 "matched": true,
942 "count": 3,
943 "details": {"methods": ["GET", "POST"]}
944 }"#;
945 let result: VerificationResult = serde_json::from_str(json).unwrap();
946 assert!(result.matched);
947 assert_eq!(result.count, 3);
948 }
949
950 #[test]
951 fn deserialize_world_state_entry() {
952 let json = r#"{
953 "key": "user.count",
954 "value": 42,
955 "updated_at": "2025-06-15T12:00:00Z"
956 }"#;
957 let entry: WorldStateEntry = serde_json::from_str(json).unwrap();
958 assert_eq!(entry.key, "user.count");
959 assert_eq!(entry.value, serde_json::json!(42));
960 }
961
962 #[test]
963 fn deserialize_federation_peer() {
964 let json = r#"{
965 "id": "peer-001",
966 "url": "http://peer1:9080",
967 "status": "connected",
968 "last_sync": "2025-06-15T12:00:00Z"
969 }"#;
970 let peer: FederationPeer = serde_json::from_str(json).unwrap();
971 assert_eq!(peer.id, "peer-001");
972 assert_eq!(peer.url, "http://peer1:9080");
973 assert_eq!(peer.status, "connected");
974 }
975
976 #[test]
977 fn deserialize_contract_diff_capture() {
978 let json = r#"{
979 "id": "diff-001",
980 "path": "/api/users",
981 "method": "GET",
982 "diff_status": "changed",
983 "captured_at": "2025-06-15T12:00:00Z"
984 }"#;
985 let capture: ContractDiffCapture = serde_json::from_str(json).unwrap();
986 assert_eq!(capture.id, "diff-001");
987 assert_eq!(capture.diff_status, "changed");
988 }
989
990 #[test]
991 fn deserialize_metrics_data() {
992 let json = r#"{
993 "requests_by_endpoint": {"/api/users": 100, "/api/orders": 50},
994 "response_time_percentiles": {"p50": 20, "p99": 200},
995 "error_rate_by_endpoint": {"/api/users": 0.01}
996 }"#;
997 let metrics: MetricsData = serde_json::from_str(json).unwrap();
998 assert_eq!(metrics.requests_by_endpoint.len(), 2);
999 assert_eq!(*metrics.requests_by_endpoint.get("/api/users").unwrap(), 100);
1000 assert_eq!(metrics.response_time_percentiles.len(), 2);
1001 }
1002
1003 #[test]
1004 fn deserialize_system_info() {
1005 let json = r#"{
1006 "version": "0.3.31",
1007 "uptime_seconds": 3600,
1008 "memory_usage_mb": 128,
1009 "cpu_usage_percent": 15.5,
1010 "active_threads": 8,
1011 "total_routes": 42,
1012 "total_fixtures": 10
1013 }"#;
1014 let sys: SystemInfo = serde_json::from_str(json).unwrap();
1015 assert_eq!(sys.version, "0.3.31");
1016 assert_eq!(sys.uptime_seconds, 3600);
1017 assert_eq!(sys.total_routes, 42);
1018 }
1019
1020 #[test]
1021 fn deserialize_health_probe() {
1022 let json = r#"{
1023 "status": "ok",
1024 "checks": {"db": true, "redis": "connected"}
1025 }"#;
1026 let probe: HealthProbe = serde_json::from_str(json).unwrap();
1027 assert_eq!(probe.status, "ok");
1028 assert_eq!(probe.checks.len(), 2);
1029 }
1030
1031 #[test]
1032 fn roundtrip_serialize_deserialize() {
1033 let original = RecorderStatus {
1034 recording: true,
1035 recorded_count: 99,
1036 };
1037 let json = serde_json::to_string(&original).unwrap();
1038 let deserialized: RecorderStatus = serde_json::from_str(&json).unwrap();
1039 assert_eq!(deserialized.recording, original.recording);
1040 assert_eq!(deserialized.recorded_count, original.recorded_count);
1041 }
1042
1043 #[test]
1044 fn api_response_with_complex_data() {
1045 let json = r#"{
1046 "success": true,
1047 "data": [
1048 {"method": "GET", "path": "/users/{id}", "priority": 1, "has_fixtures": false, "request_count": 0, "error_count": 0},
1049 {"method": "POST", "path": "/users", "priority": 2, "has_fixtures": true, "request_count": 5, "error_count": 1}
1050 ],
1051 "error": null
1052 }"#;
1053 let resp: ApiResponse<Vec<RouteInfo>> = serde_json::from_str(json).unwrap();
1054 assert!(resp.success);
1055 let routes = resp.data.unwrap();
1056 assert_eq!(routes.len(), 2);
1057 assert_eq!(routes[0].path, "/users/{id}");
1058 assert_eq!(routes[1].request_count, 5);
1059 }
1060}