mockforge_ui/
models.rs

1//! Data models for the admin UI
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6/// Server status information
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct ServerStatus {
9    /// Server type (HTTP, WebSocket, gRPC)
10    pub server_type: String,
11    /// Server address
12    pub address: Option<String>,
13    /// Whether server is running
14    pub running: bool,
15    /// Start time
16    pub start_time: Option<chrono::DateTime<chrono::Utc>>,
17    /// Uptime in seconds
18    pub uptime_seconds: Option<u64>,
19    /// Number of active connections
20    pub active_connections: u64,
21    /// Total requests served
22    pub total_requests: u64,
23}
24
25/// Route information
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct RouteInfo {
28    /// HTTP method
29    pub method: Option<String>,
30    /// Route path
31    pub path: String,
32    /// Route priority
33    pub priority: i32,
34    /// Whether route has fixtures
35    pub has_fixtures: bool,
36    /// Latency profile
37    pub latency_ms: Option<u64>,
38    /// Request count
39    pub request_count: u64,
40    /// Last request time
41    pub last_request: Option<chrono::DateTime<chrono::Utc>>,
42    /// Error count
43    pub error_count: u64,
44}
45
46/// Request log entry
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct RequestLog {
49    /// Request ID
50    pub id: String,
51    /// Timestamp
52    pub timestamp: chrono::DateTime<chrono::Utc>,
53    /// HTTP method
54    pub method: String,
55    /// Request path
56    pub path: String,
57    /// Response status code
58    pub status_code: u16,
59    /// Response time in milliseconds
60    pub response_time_ms: u64,
61    /// Client IP address
62    pub client_ip: Option<String>,
63    /// User agent
64    pub user_agent: Option<String>,
65    /// Request headers (filtered)
66    pub headers: HashMap<String, String>,
67    /// Response size in bytes
68    pub response_size_bytes: u64,
69    /// Error message (if any)
70    pub error_message: Option<String>,
71}
72
73/// System information
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct SystemInfo {
76    /// MockForge version
77    pub version: String,
78    /// Uptime in seconds
79    pub uptime_seconds: u64,
80    /// Memory usage in MB
81    pub memory_usage_mb: u64,
82    /// CPU usage percentage
83    pub cpu_usage_percent: f64,
84    /// Number of active threads
85    pub active_threads: usize,
86    /// Total routes configured
87    pub total_routes: usize,
88    /// Total fixtures available
89    pub total_fixtures: usize,
90}
91
92/// Latency profile configuration
93#[derive(Debug, Clone, Serialize, Deserialize)]
94pub struct LatencyProfile {
95    /// Profile name
96    pub name: String,
97    /// Base latency in milliseconds
98    pub base_ms: u64,
99    /// Jitter range in milliseconds
100    pub jitter_ms: u64,
101    /// Tag-based overrides
102    pub tag_overrides: HashMap<String, u64>,
103}
104
105/// Fault injection configuration
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct FaultConfig {
108    /// Whether fault injection is enabled
109    pub enabled: bool,
110    /// Failure rate (0.0 to 1.0)
111    pub failure_rate: f64,
112    /// HTTP status codes for failures
113    pub status_codes: Vec<u16>,
114    /// Current active failures
115    pub active_failures: u64,
116}
117
118/// Proxy configuration
119#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct ProxyConfig {
121    /// Whether proxy is enabled
122    pub enabled: bool,
123    /// Upstream URL
124    pub upstream_url: Option<String>,
125    /// Request timeout seconds
126    pub timeout_seconds: u64,
127    /// Total requests proxied
128    pub requests_proxied: u64,
129}
130
131/// Bandwidth configuration
132#[derive(Debug, Clone, Serialize, Deserialize)]
133pub struct BandwidthConfig {
134    /// Whether bandwidth throttling is enabled
135    pub enabled: bool,
136    /// Maximum bandwidth in bytes per second
137    pub max_bytes_per_sec: u64,
138    /// Burst capacity in bytes
139    pub burst_capacity_bytes: u64,
140    /// Tag-based overrides
141    pub tag_overrides: HashMap<String, u64>,
142}
143
144/// Burst loss configuration
145#[derive(Debug, Clone, Serialize, Deserialize)]
146pub struct BurstLossConfig {
147    /// Whether burst loss is enabled
148    pub enabled: bool,
149    /// Probability of entering burst (0.0 to 1.0)
150    pub burst_probability: f64,
151    /// Duration of burst in milliseconds
152    pub burst_duration_ms: u64,
153    /// Loss rate during burst (0.0 to 1.0)
154    pub loss_rate_during_burst: f64,
155    /// Recovery time between bursts in milliseconds
156    pub recovery_time_ms: u64,
157    /// Tag-based overrides
158    pub tag_overrides: HashMap<String, BurstLossOverride>,
159}
160
161/// Burst loss override for specific tags
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct BurstLossOverride {
164    pub burst_probability: f64,
165    pub burst_duration_ms: u64,
166    pub loss_rate_during_burst: f64,
167    pub recovery_time_ms: u64,
168}
169
170/// Traffic shaping configuration
171#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct TrafficShapingConfig {
173    /// Whether traffic shaping is enabled
174    pub enabled: bool,
175    /// Bandwidth configuration
176    pub bandwidth: BandwidthConfig,
177    /// Burst loss configuration
178    pub burst_loss: BurstLossConfig,
179}
180
181/// Simple metrics data for admin dashboard
182#[derive(Debug, Clone, Serialize, Deserialize)]
183pub struct SimpleMetricsData {
184    /// Total requests served
185    pub total_requests: u64,
186    /// Active requests currently being processed
187    pub active_requests: u64,
188    /// Average response time in milliseconds
189    pub average_response_time: f64,
190    /// Error rate (0.0 to 1.0)
191    pub error_rate: f64,
192}
193
194/// Dashboard system information
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct DashboardSystemInfo {
197    /// Operating system
198    pub os: String,
199    /// Architecture
200    pub arch: String,
201    /// Uptime in seconds
202    pub uptime: u64,
203    /// Memory usage in bytes
204    pub memory_usage: u64,
205}
206
207/// Dashboard data
208#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct DashboardData {
210    /// Server information
211    pub server_info: ServerInfo,
212    /// System information
213    pub system_info: DashboardSystemInfo,
214    /// Metrics data
215    pub metrics: SimpleMetricsData,
216    /// Server status information
217    pub servers: Vec<ServerStatus>,
218    /// Recent logs
219    pub recent_logs: Vec<RequestLog>,
220    /// System information (for JS compatibility)
221    pub system: SystemInfo,
222}
223
224/// API response wrapper
225#[derive(Debug, Clone, Serialize, Deserialize)]
226pub struct ApiResponse<T> {
227    /// Whether request was successful
228    pub success: bool,
229    /// Response data
230    pub data: Option<T>,
231    /// Error message (if any)
232    pub error: Option<String>,
233    /// Response timestamp
234    pub timestamp: chrono::DateTime<chrono::Utc>,
235}
236
237impl<T> ApiResponse<T> {
238    /// Create a successful response
239    pub fn success(data: T) -> Self {
240        Self {
241            success: true,
242            data: Some(data),
243            error: None,
244            timestamp: chrono::Utc::now(),
245        }
246    }
247
248    /// Create an error response
249    pub fn error(message: String) -> Self {
250        Self {
251            success: false,
252            data: None,
253            error: Some(message),
254            timestamp: chrono::Utc::now(),
255        }
256    }
257}
258
259/// Configuration update request
260#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct ConfigUpdate {
262    /// Configuration type
263    pub config_type: String,
264    /// Configuration data
265    pub data: serde_json::Value,
266}
267
268/// Route management request
269#[derive(Debug, Clone, Serialize, Deserialize)]
270pub struct RouteUpdate {
271    /// Route path
272    pub path: String,
273    /// HTTP method (optional)
274    pub method: Option<String>,
275    /// Update operation
276    pub operation: String,
277    /// Update data
278    pub data: Option<serde_json::Value>,
279}
280
281/// Log filter options
282#[derive(Debug, Clone, Serialize, Deserialize)]
283pub struct LogFilter {
284    /// Filter by HTTP method
285    pub method: Option<String>,
286    /// Filter by path pattern
287    pub path_pattern: Option<String>,
288    /// Filter by status code
289    pub status_code: Option<u16>,
290    /// Filter by time range (hours ago)
291    pub hours_ago: Option<u64>,
292    /// Maximum number of results
293    pub limit: Option<usize>,
294}
295
296impl Default for LogFilter {
297    fn default() -> Self {
298        Self {
299            method: None,
300            path_pattern: None,
301            status_code: None,
302            hours_ago: Some(24),
303            limit: Some(100),
304        }
305    }
306}
307
308/// Performance metrics data with detailed percentile analysis
309#[derive(Debug, Clone, Serialize, Deserialize)]
310pub struct MetricsData {
311    /// Request count by endpoint
312    pub requests_by_endpoint: HashMap<String, u64>,
313    /// Response time percentiles (p50, p75, p90, p95, p99, p999)
314    pub response_time_percentiles: HashMap<String, u64>,
315    /// Per-endpoint percentiles for detailed analysis
316    #[serde(skip_serializing_if = "Option::is_none")]
317    pub endpoint_percentiles: Option<HashMap<String, HashMap<String, u64>>>,
318    /// Latency over time (time-series data)
319    #[serde(skip_serializing_if = "Option::is_none")]
320    pub latency_over_time: Option<Vec<(chrono::DateTime<chrono::Utc>, u64)>>,
321    /// Error rate by endpoint
322    pub error_rate_by_endpoint: HashMap<String, f64>,
323    /// Memory usage over time
324    pub memory_usage_over_time: Vec<(chrono::DateTime<chrono::Utc>, u64)>,
325    /// CPU usage over time
326    pub cpu_usage_over_time: Vec<(chrono::DateTime<chrono::Utc>, f64)>,
327}
328
329/// Validation settings
330#[derive(Debug, Clone, Serialize, Deserialize)]
331pub struct ValidationSettings {
332    /// Validation mode: "enforce", "warn", or "off"
333    pub mode: String,
334    /// Whether to aggregate errors
335    pub aggregate_errors: bool,
336    /// Whether to validate responses
337    pub validate_responses: bool,
338    /// Per-route validation overrides
339    pub overrides: HashMap<String, String>,
340}
341
342/// Validation update request
343#[derive(Debug, Clone, Serialize, Deserialize)]
344pub struct ValidationUpdate {
345    /// Validation mode
346    pub mode: String,
347    /// Whether to aggregate errors
348    pub aggregate_errors: bool,
349    /// Whether to validate responses
350    pub validate_responses: bool,
351    /// Per-route validation overrides
352    pub overrides: Option<HashMap<String, String>>,
353}
354
355/// Log entry for admin UI
356#[derive(Debug, Clone, Serialize, Deserialize)]
357pub struct LogEntry {
358    /// Request timestamp
359    pub timestamp: chrono::DateTime<chrono::Utc>,
360    /// HTTP status code
361    pub status: u16,
362    /// HTTP method
363    pub method: String,
364    /// Request URL/path
365    pub url: String,
366    /// Response time in milliseconds
367    pub response_time: u64,
368    /// Response size in bytes
369    pub size: u64,
370}
371
372/// Health check response
373#[derive(Debug, Clone, Serialize, Deserialize)]
374pub struct HealthCheck {
375    /// Overall health status
376    pub status: String,
377    /// Individual service health
378    pub services: HashMap<String, String>,
379    /// Last health check time
380    pub last_check: chrono::DateTime<chrono::Utc>,
381    /// Any health issues
382    pub issues: Vec<String>,
383}
384
385impl HealthCheck {
386    /// Create a healthy status
387    pub fn healthy() -> Self {
388        Self {
389            status: "healthy".to_string(),
390            services: HashMap::new(),
391            last_check: chrono::Utc::now(),
392            issues: Vec::new(),
393        }
394    }
395
396    /// Create an unhealthy status
397    pub fn unhealthy(issues: Vec<String>) -> Self {
398        Self {
399            status: "unhealthy".to_string(),
400            services: HashMap::new(),
401            last_check: chrono::Utc::now(),
402            issues,
403        }
404    }
405
406    /// Add service status
407    pub fn with_service(mut self, name: String, status: String) -> Self {
408        self.services.insert(name, status);
409        self
410    }
411}
412
413/// Workspace summary information
414#[derive(Debug, Clone, Serialize, Deserialize)]
415pub struct WorkspaceSummary {
416    /// Workspace ID
417    pub id: String,
418    /// Workspace name
419    pub name: String,
420    /// Description
421    pub description: Option<String>,
422    /// Whether this is the active workspace
423    pub active: bool,
424    /// Number of folders
425    pub folder_count: usize,
426    /// Number of requests
427    pub request_count: usize,
428    /// Created timestamp
429    pub created_at: chrono::DateTime<chrono::Utc>,
430    /// Updated timestamp
431    pub updated_at: chrono::DateTime<chrono::Utc>,
432}
433
434/// Folder summary information
435#[derive(Debug, Clone, Serialize, Deserialize)]
436pub struct FolderSummary {
437    /// Folder ID
438    pub id: String,
439    /// Folder name
440    pub name: String,
441    /// Description
442    pub description: Option<String>,
443    /// Parent folder ID (None if root)
444    pub parent_id: Option<String>,
445    /// Number of subfolders
446    pub subfolder_count: usize,
447    /// Number of requests
448    pub request_count: usize,
449    /// Created timestamp
450    pub created_at: chrono::DateTime<chrono::Utc>,
451    /// Updated timestamp
452    pub updated_at: chrono::DateTime<chrono::Utc>,
453}
454
455/// Request summary information
456#[derive(Debug, Clone, Serialize, Deserialize)]
457pub struct RequestSummary {
458    /// Request ID
459    pub id: String,
460    /// Request name
461    pub name: String,
462    /// Description
463    pub description: Option<String>,
464    /// HTTP method
465    pub method: String,
466    /// Request path
467    pub path: String,
468    /// Response status code
469    pub status_code: u16,
470    /// Created timestamp
471    pub created_at: chrono::DateTime<chrono::Utc>,
472    /// Updated timestamp
473    pub updated_at: chrono::DateTime<chrono::Utc>,
474}
475
476/// Workspace detailed information
477#[derive(Debug, Clone, Serialize, Deserialize)]
478pub struct WorkspaceDetail {
479    /// Workspace summary
480    pub summary: WorkspaceSummary,
481    /// Root folders
482    pub folders: Vec<FolderSummary>,
483    /// Root requests
484    pub requests: Vec<RequestSummary>,
485}
486
487/// Folder detailed information
488#[derive(Debug, Clone, Serialize, Deserialize)]
489pub struct FolderDetail {
490    /// Folder summary
491    pub summary: FolderSummary,
492    /// Subfolders
493    pub subfolders: Vec<FolderSummary>,
494    /// Requests in this folder
495    pub requests: Vec<RequestSummary>,
496}
497
498/// Create workspace request
499#[derive(Debug, Clone, Serialize, Deserialize)]
500pub struct CreateWorkspaceRequest {
501    /// Workspace name
502    pub name: String,
503    /// Description (optional)
504    pub description: Option<String>,
505}
506
507/// Create folder request
508#[derive(Debug, Clone, Serialize, Deserialize)]
509pub struct CreateFolderRequest {
510    /// Folder name
511    pub name: String,
512    /// Description (optional)
513    pub description: Option<String>,
514    /// Parent folder ID (optional)
515    pub parent_id: Option<String>,
516}
517
518/// Create request request
519#[derive(Debug, Clone, Serialize, Deserialize)]
520pub struct CreateRequestRequest {
521    /// Request name
522    pub name: String,
523    /// Description (optional)
524    pub description: Option<String>,
525    /// HTTP method
526    pub method: String,
527    /// Request path
528    pub path: String,
529    /// Response status code
530    pub status_code: Option<u16>,
531    /// Response body
532    pub response_body: Option<String>,
533    /// Folder ID (optional)
534    pub folder_id: Option<String>,
535}
536
537/// Import to workspace request
538#[derive(Debug, Clone, Serialize, Deserialize)]
539pub struct ImportToWorkspaceRequest {
540    /// Import format (postman, insomnia, curl)
541    pub format: String,
542    /// Import data (file content or URL)
543    pub data: String,
544    /// Folder ID to import into (optional)
545    pub folder_id: Option<String>,
546    /// Whether to create folders from import structure
547    pub create_folders: Option<bool>,
548    /// Indices of routes to import (for selective import)
549    pub selected_routes: Option<Vec<usize>>,
550}
551
552/// Export workspaces request
553#[derive(Debug, Clone, Serialize, Deserialize)]
554pub struct ExportWorkspacesRequest {
555    /// Workspace IDs to export
556    pub workspace_ids: Vec<String>,
557}
558
559/// Workspace export data
560#[derive(Debug, Clone, Serialize, Deserialize)]
561pub struct WorkspaceExportData {
562    /// Exported workspaces
563    pub workspaces: Vec<mockforge_core::Workspace>,
564    /// Export version
565    pub version: String,
566    /// Export timestamp
567    pub exported_at: chrono::DateTime<chrono::Utc>,
568    /// Exporter version
569    pub exporter_version: String,
570}
571
572/// Environment color information
573#[derive(Debug, Clone, Serialize, Deserialize)]
574pub struct EnvironmentColor {
575    /// Hex color code (e.g., "#FF5733")
576    pub hex: String,
577    /// Optional color name for accessibility
578    pub name: Option<String>,
579}
580
581/// Environment summary information
582#[derive(Debug, Clone, Serialize, Deserialize)]
583pub struct EnvironmentSummary {
584    /// Environment ID
585    pub id: String,
586    /// Environment name
587    pub name: String,
588    /// Description
589    pub description: Option<String>,
590    /// Color for visual distinction
591    pub color: Option<EnvironmentColor>,
592    /// Number of variables
593    pub variable_count: usize,
594    /// Whether this is the active environment
595    pub active: bool,
596    /// Whether this is the global environment
597    pub is_global: bool,
598    /// Created timestamp
599    pub created_at: chrono::DateTime<chrono::Utc>,
600    /// Updated timestamp
601    pub updated_at: chrono::DateTime<chrono::Utc>,
602}
603
604/// Environment variable information
605#[derive(Debug, Clone, Serialize, Deserialize)]
606pub struct EnvironmentVariable {
607    /// Variable name
608    pub name: String,
609    /// Variable value
610    pub value: String,
611    /// Whether this variable is from the global environment
612    pub from_global: bool,
613}
614
615/// Create environment request
616#[derive(Debug, Clone, Serialize, Deserialize)]
617pub struct CreateEnvironmentRequest {
618    /// Environment name
619    pub name: String,
620    /// Description
621    pub description: Option<String>,
622}
623
624/// Update environment request
625#[derive(Debug, Clone, Serialize, Deserialize)]
626pub struct UpdateEnvironmentRequest {
627    /// Environment name
628    pub name: Option<String>,
629    /// Description
630    pub description: Option<String>,
631    /// Color
632    pub color: Option<EnvironmentColor>,
633}
634
635/// Set variable request
636#[derive(Debug, Clone, Serialize, Deserialize)]
637pub struct SetVariableRequest {
638    /// Variable name
639    pub name: String,
640    /// Variable value
641    pub value: String,
642}
643
644/// Directory sync configuration
645#[derive(Debug, Clone, Serialize, Deserialize)]
646pub struct SyncConfig {
647    /// Enable directory syncing for this workspace
648    pub enabled: bool,
649    /// Target directory for sync (relative or absolute path)
650    pub target_directory: Option<String>,
651    /// Directory structure to use (flat, nested, grouped)
652    pub directory_structure: SyncDirectoryStructure,
653    /// Auto-sync direction (one-way workspace→directory, bidirectional, or manual)
654    pub sync_direction: SyncDirection,
655    /// Whether to include metadata files
656    pub include_metadata: bool,
657    /// Filesystem monitoring enabled for real-time sync
658    pub realtime_monitoring: bool,
659    /// Custom filename pattern for exported files
660    pub filename_pattern: String,
661    /// Regular expression for excluding workspaces/requests
662    pub exclude_pattern: Option<String>,
663    /// Force overwrite existing files during sync
664    pub force_overwrite: bool,
665}
666
667/// Directory structure options for sync
668#[derive(Debug, Clone, Serialize, Deserialize)]
669pub enum SyncDirectoryStructure {
670    /// All workspaces in flat structure: workspace-name.yaml
671    Flat,
672    /// Nested by workspace: workspaces/{name}/workspace.yaml + requests/
673    Nested,
674    /// Grouped by type: requests/, responses/, metadata/
675    Grouped,
676}
677
678/// Sync direction options
679#[derive(Debug, Clone, Serialize, Deserialize)]
680pub enum SyncDirection {
681    /// Manual sync only (one-off operations)
682    Manual,
683    /// One-way: workspace changes sync silently to directory
684    WorkspaceToDirectory,
685    /// Bidirectional: changes in either direction trigger sync
686    Bidirectional,
687}
688
689impl From<mockforge_core::workspace::SyncDirection> for SyncDirection {
690    fn from(core: mockforge_core::workspace::SyncDirection) -> Self {
691        match core {
692            mockforge_core::workspace::SyncDirection::Manual => Self::Manual,
693            mockforge_core::workspace::SyncDirection::WorkspaceToDirectory => {
694                Self::WorkspaceToDirectory
695            }
696            mockforge_core::workspace::SyncDirection::Bidirectional => Self::Bidirectional,
697        }
698    }
699}
700
701/// Sync status information
702#[derive(Debug, Clone, Serialize, Deserialize)]
703pub struct SyncStatus {
704    /// Workspace ID
705    pub workspace_id: String,
706    /// Whether sync is enabled
707    pub enabled: bool,
708    /// Target directory
709    pub target_directory: Option<String>,
710    /// Current sync direction
711    pub sync_direction: SyncDirection,
712    /// Whether real-time monitoring is active
713    pub realtime_monitoring: bool,
714    /// Last sync timestamp
715    pub last_sync: Option<chrono::DateTime<chrono::Utc>>,
716    /// Sync status message
717    pub status: String,
718}
719
720/// Sync change information
721#[derive(Debug, Clone, Serialize, Deserialize)]
722pub struct SyncChange {
723    /// Change type
724    pub change_type: String,
725    /// File path
726    pub path: String,
727    /// Change description
728    pub description: String,
729    /// Whether this change requires confirmation
730    pub requires_confirmation: bool,
731}
732
733/// Configure sync request
734#[derive(Debug, Clone, Serialize, Deserialize)]
735pub struct ConfigureSyncRequest {
736    /// Target directory
737    pub target_directory: String,
738    /// Sync direction
739    pub sync_direction: SyncDirection,
740    /// Enable real-time monitoring
741    pub realtime_monitoring: bool,
742    /// Directory structure
743    pub directory_structure: Option<SyncDirectoryStructure>,
744    /// Filename pattern
745    pub filename_pattern: Option<String>,
746}
747
748/// Confirm sync changes request
749#[derive(Debug, Clone, Serialize, Deserialize)]
750pub struct ConfirmSyncChangesRequest {
751    /// Workspace ID
752    pub workspace_id: String,
753    /// Changes to confirm
754    pub changes: Vec<SyncChange>,
755    /// Whether to apply all changes
756    pub apply_all: bool,
757}
758
759/// Autocomplete suggestion
760#[derive(Debug, Clone, Serialize, Deserialize)]
761pub struct AutocompleteSuggestion {
762    /// Suggestion text
763    pub text: String,
764    /// Suggestion type (e.g., "variable", "template")
765    pub kind: String,
766    /// Optional description
767    pub description: Option<String>,
768}
769
770/// Autocomplete request
771#[derive(Debug, Clone, Serialize, Deserialize)]
772pub struct AutocompleteRequest {
773    /// Current input text
774    pub input: String,
775    /// Cursor position in the text
776    pub cursor_position: usize,
777    /// Context type (e.g., "header", "body", "url")
778    pub context: Option<String>,
779}
780
781/// Autocomplete response
782#[derive(Debug, Clone, Serialize, Deserialize)]
783pub struct AutocompleteResponse {
784    /// List of suggestions
785    pub suggestions: Vec<AutocompleteSuggestion>,
786    /// Start position of the token being completed
787    pub start_position: usize,
788    /// End position of the token being completed
789    pub end_position: usize,
790}
791
792/// Time series data point
793#[derive(Debug, Clone, Serialize, Deserialize)]
794pub struct TimeSeriesPoint {
795    /// Timestamp
796    pub timestamp: chrono::DateTime<chrono::Utc>,
797    /// Value
798    pub value: f64,
799}
800
801/// Time series data
802#[derive(Debug, Clone, Serialize, Deserialize)]
803pub struct TimeSeriesData {
804    /// Data points
805    pub points: Vec<TimeSeriesPoint>,
806    /// Metric name
807    pub metric: String,
808}
809
810/// Restart status
811#[derive(Debug, Clone, Serialize, Deserialize)]
812pub struct RestartStatus {
813    /// Whether restart is in progress
814    pub restarting: bool,
815    /// Restart progress (0.0 to 1.0)
816    pub progress: f64,
817    /// Status message
818    pub message: String,
819}
820
821/// Smoke test result
822#[derive(Debug, Clone, Serialize, Deserialize)]
823pub struct SmokeTestResult {
824    /// Test name
825    pub test_name: String,
826    /// Whether test passed
827    pub passed: bool,
828    /// Response time in milliseconds
829    pub response_time_ms: Option<u64>,
830    /// Error message (if failed)
831    pub error_message: Option<String>,
832}
833
834/// Smoke test context
835#[derive(Debug, Clone, Serialize, Deserialize)]
836pub struct SmokeTestContext {
837    /// Test suite name
838    pub suite_name: String,
839    /// Total tests
840    pub total_tests: usize,
841    /// Passed tests
842    pub passed_tests: usize,
843    /// Failed tests
844    pub failed_tests: usize,
845    /// Start time
846    pub start_time: chrono::DateTime<chrono::Utc>,
847    /// End time
848    pub end_time: Option<chrono::DateTime<chrono::Utc>>,
849}
850
851/// Configuration state
852#[derive(Debug, Clone, Serialize, Deserialize)]
853pub struct ConfigurationState {
854    /// Whether configuration is valid
855    pub valid: bool,
856    /// Configuration errors
857    pub errors: Vec<String>,
858    /// Configuration warnings
859    pub warnings: Vec<String>,
860}
861
862/// Import history entry
863#[derive(Debug, Clone, Serialize, Deserialize)]
864pub struct ImportHistoryEntry {
865    /// Entry ID
866    pub id: String,
867    /// Import format
868    pub format: String,
869    /// Import timestamp
870    pub timestamp: chrono::DateTime<chrono::Utc>,
871    /// Number of routes imported
872    pub routes_count: usize,
873    /// Number of variables imported
874    pub variables_count: usize,
875    /// Number of warnings
876    pub warnings_count: usize,
877    /// Whether import was successful
878    pub success: bool,
879    /// Filename (if applicable)
880    pub filename: Option<String>,
881    /// Environment name
882    pub environment: Option<String>,
883    /// Base URL
884    pub base_url: Option<String>,
885    /// Error message (if failed)
886    pub error_message: Option<String>,
887}
888
889/// Fixture information
890#[derive(Debug, Clone, Serialize, Deserialize)]
891pub struct FixtureInfo {
892    /// Fixture ID
893    pub id: String,
894    /// Fixture name
895    pub name: String,
896    /// Fixture path
897    pub path: String,
898    /// File size in bytes
899    pub size_bytes: u64,
900    /// Last modified timestamp
901    pub last_modified: chrono::DateTime<chrono::Utc>,
902    /// Content type
903    pub content_type: Option<String>,
904}
905
906/// Fixture delete request
907#[derive(Debug, Clone, Serialize, Deserialize)]
908pub struct FixtureDeleteRequest {
909    /// Fixture ID to delete
910    pub fixture_id: String,
911}
912
913/// Fixture bulk delete request
914#[derive(Debug, Clone, Serialize, Deserialize)]
915pub struct FixtureBulkDeleteRequest {
916    /// Fixture IDs to delete
917    pub fixture_ids: Vec<String>,
918}
919
920/// Fixture bulk delete result
921#[derive(Debug, Clone, Serialize, Deserialize)]
922pub struct FixtureBulkDeleteResult {
923    /// Successfully deleted fixture IDs
924    pub deleted: Vec<String>,
925    /// Failed deletions with error messages
926    pub failed: HashMap<String, String>,
927}
928
929/// Import route
930#[derive(Debug, Clone, Serialize, Deserialize)]
931pub struct ImportRoute {
932    /// HTTP method
933    pub method: String,
934    /// Route path
935    pub path: String,
936    /// Request headers
937    pub headers: HashMap<String, String>,
938    /// Request body
939    pub body: Option<String>,
940    /// Expected response
941    pub response: ImportResponse,
942}
943
944/// Import response
945#[derive(Debug, Clone, Serialize, Deserialize)]
946pub struct ImportResponse {
947    /// HTTP status code
948    pub status: u16,
949    /// Response headers
950    pub headers: HashMap<String, String>,
951    /// Response body
952    pub body: serde_json::Value,
953}
954
955/// Import result
956#[derive(Debug, Clone, Serialize, Deserialize)]
957pub struct ImportResult {
958    /// Imported routes
959    pub routes: Vec<ImportRoute>,
960    /// Import warnings
961    pub warnings: Vec<String>,
962    /// Import errors
963    pub errors: Vec<String>,
964}
965
966/// Insomnia import result
967#[derive(Debug, Clone, Serialize, Deserialize)]
968pub struct InsomniaImportResult {
969    /// Imported routes
970    pub routes: Vec<ImportRoute>,
971    /// Environment variables
972    pub variables: HashMap<String, String>,
973    /// Import warnings
974    pub warnings: Vec<String>,
975}
976
977/// Server information
978#[derive(Debug, Clone, Serialize, Deserialize)]
979pub struct ServerInfo {
980    /// Server version
981    pub version: String,
982    /// Build timestamp
983    pub build_time: String,
984    /// Git SHA
985    pub git_sha: String,
986    /// HTTP server address (optional)
987    pub http_server: Option<String>,
988    /// WebSocket server address (optional)
989    pub ws_server: Option<String>,
990    /// gRPC server address (optional)
991    pub grpc_server: Option<String>,
992    /// GraphQL server address (optional)
993    pub graphql_server: Option<String>,
994    /// Whether API endpoints are enabled
995    pub api_enabled: bool,
996    /// Admin server port
997    pub admin_port: u16,
998}
999
1000#[cfg(test)]
1001mod tests {
1002    use super::*;
1003
1004    #[test]
1005    fn test_api_response_success() {
1006        let data = "test data".to_string();
1007        let response = ApiResponse::success(data.clone());
1008
1009        assert!(response.success);
1010        assert_eq!(response.data, Some(data));
1011        assert!(response.error.is_none());
1012    }
1013
1014    #[test]
1015    fn test_api_response_error() {
1016        let error_msg = "Something went wrong".to_string();
1017        let response: ApiResponse<String> = ApiResponse::error(error_msg.clone());
1018
1019        assert!(!response.success);
1020        assert!(response.data.is_none());
1021        assert_eq!(response.error, Some(error_msg));
1022    }
1023
1024    #[test]
1025    fn test_health_check_healthy() {
1026        let health = HealthCheck::healthy();
1027
1028        assert_eq!(health.status, "healthy");
1029        assert!(health.issues.is_empty());
1030        assert!(health.services.is_empty());
1031    }
1032
1033    #[test]
1034    fn test_health_check_unhealthy() {
1035        let issues = vec!["Database down".to_string(), "Cache error".to_string()];
1036        let health = HealthCheck::unhealthy(issues.clone());
1037
1038        assert_eq!(health.status, "unhealthy");
1039        assert_eq!(health.issues, issues);
1040    }
1041
1042    #[test]
1043    fn test_health_check_with_service() {
1044        let health = HealthCheck::healthy()
1045            .with_service("http".to_string(), "running".to_string())
1046            .with_service("grpc".to_string(), "running".to_string());
1047
1048        assert_eq!(health.services.len(), 2);
1049        assert_eq!(health.services.get("http"), Some(&"running".to_string()));
1050    }
1051
1052    #[test]
1053    fn test_log_filter_default() {
1054        let filter = LogFilter::default();
1055
1056        assert!(filter.method.is_none());
1057        assert!(filter.path_pattern.is_none());
1058        assert_eq!(filter.hours_ago, Some(24));
1059        assert_eq!(filter.limit, Some(100));
1060    }
1061
1062    #[test]
1063    fn test_server_status() {
1064        let status = ServerStatus {
1065            server_type: "HTTP".to_string(),
1066            address: Some("127.0.0.1:3000".to_string()),
1067            running: true,
1068            start_time: Some(chrono::Utc::now()),
1069            uptime_seconds: Some(3600),
1070            active_connections: 10,
1071            total_requests: 1000,
1072        };
1073
1074        assert_eq!(status.server_type, "HTTP");
1075        assert!(status.running);
1076        assert_eq!(status.active_connections, 10);
1077    }
1078
1079    #[test]
1080    fn test_route_info() {
1081        let route = RouteInfo {
1082            method: Some("GET".to_string()),
1083            path: "/api/users".to_string(),
1084            priority: 100,
1085            has_fixtures: true,
1086            latency_ms: Some(50),
1087            request_count: 500,
1088            last_request: None,
1089            error_count: 5,
1090        };
1091
1092        assert_eq!(route.method, Some("GET".to_string()));
1093        assert_eq!(route.path, "/api/users");
1094        assert!(route.has_fixtures);
1095    }
1096
1097    #[test]
1098    fn test_sync_direction_conversion() {
1099        let manual = mockforge_core::workspace::SyncDirection::Manual;
1100        let ui_manual: SyncDirection = manual.into();
1101        assert!(matches!(ui_manual, SyncDirection::Manual));
1102    }
1103
1104    #[test]
1105    fn test_environment_color() {
1106        let color = EnvironmentColor {
1107            hex: "#FF5733".to_string(),
1108            name: Some("Orange".to_string()),
1109        };
1110
1111        assert_eq!(color.hex, "#FF5733");
1112        assert_eq!(color.name, Some("Orange".to_string()));
1113    }
1114}