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