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