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