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