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