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