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