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