use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerStatus {
pub server_type: String,
pub address: Option<String>,
pub running: bool,
pub start_time: Option<chrono::DateTime<chrono::Utc>>,
pub uptime_seconds: Option<u64>,
pub active_connections: u64,
pub total_requests: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RouteInfo {
pub method: Option<String>,
pub path: String,
pub priority: i32,
pub has_fixtures: bool,
pub latency_ms: Option<u64>,
pub request_count: u64,
pub last_request: Option<chrono::DateTime<chrono::Utc>>,
pub error_count: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RequestLog {
pub id: String,
pub timestamp: chrono::DateTime<chrono::Utc>,
pub method: String,
pub path: String,
pub status_code: u16,
pub response_time_ms: u64,
pub client_ip: Option<String>,
pub user_agent: Option<String>,
pub headers: HashMap<String, String>,
pub response_size_bytes: u64,
pub error_message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemInfo {
pub version: String,
pub uptime_seconds: u64,
pub memory_usage_mb: u64,
pub cpu_usage_percent: f64,
pub active_threads: usize,
pub total_routes: usize,
pub total_fixtures: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LatencyProfile {
pub name: String,
pub base_ms: u64,
pub jitter_ms: u64,
pub tag_overrides: HashMap<String, u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FaultConfig {
pub enabled: bool,
pub failure_rate: f64,
pub status_codes: Vec<u16>,
pub active_failures: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProxyConfig {
pub enabled: bool,
pub upstream_url: Option<String>,
pub timeout_seconds: u64,
pub requests_proxied: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BandwidthConfig {
pub enabled: bool,
pub max_bytes_per_sec: u64,
pub burst_capacity_bytes: u64,
pub tag_overrides: HashMap<String, u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BurstLossConfig {
pub enabled: bool,
pub burst_probability: f64,
pub burst_duration_ms: u64,
pub loss_rate_during_burst: f64,
pub recovery_time_ms: u64,
pub tag_overrides: HashMap<String, BurstLossOverride>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BurstLossOverride {
pub burst_probability: f64,
pub burst_duration_ms: u64,
pub loss_rate_during_burst: f64,
pub recovery_time_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TrafficShapingConfig {
pub enabled: bool,
pub bandwidth: BandwidthConfig,
pub burst_loss: BurstLossConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SimpleMetricsData {
pub total_requests: u64,
pub active_requests: u64,
pub average_response_time: f64,
pub error_rate: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DashboardSystemInfo {
pub os: String,
pub arch: String,
pub uptime: u64,
pub memory_usage: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DashboardData {
pub server_info: ServerInfo,
pub system_info: DashboardSystemInfo,
pub metrics: SimpleMetricsData,
pub servers: Vec<ServerStatus>,
pub recent_logs: Vec<RequestLog>,
pub system: SystemInfo,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiResponse<T> {
pub success: bool,
pub data: Option<T>,
pub error: Option<String>,
pub timestamp: chrono::DateTime<chrono::Utc>,
}
impl<T> ApiResponse<T> {
pub fn success(data: T) -> Self {
Self {
success: true,
data: Some(data),
error: None,
timestamp: chrono::Utc::now(),
}
}
pub fn error(message: String) -> Self {
Self {
success: false,
data: None,
error: Some(message),
timestamp: chrono::Utc::now(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigUpdate {
pub config_type: String,
pub data: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RouteUpdate {
pub path: String,
pub method: Option<String>,
pub operation: String,
pub data: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogFilter {
pub method: Option<String>,
pub path_pattern: Option<String>,
pub status_code: Option<u16>,
pub hours_ago: Option<u64>,
pub limit: Option<usize>,
}
impl Default for LogFilter {
fn default() -> Self {
Self {
method: None,
path_pattern: None,
status_code: None,
hours_ago: Some(24),
limit: Some(100),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MetricsData {
pub requests_by_endpoint: HashMap<String, u64>,
pub response_time_percentiles: HashMap<String, u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub endpoint_percentiles: Option<HashMap<String, HashMap<String, u64>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub latency_over_time: Option<Vec<(chrono::DateTime<chrono::Utc>, u64)>>,
pub error_rate_by_endpoint: HashMap<String, f64>,
pub memory_usage_over_time: Vec<(chrono::DateTime<chrono::Utc>, u64)>,
pub cpu_usage_over_time: Vec<(chrono::DateTime<chrono::Utc>, f64)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationSettings {
pub mode: String,
pub aggregate_errors: bool,
pub validate_responses: bool,
pub overrides: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ValidationUpdate {
pub mode: String,
pub aggregate_errors: bool,
pub validate_responses: bool,
pub overrides: Option<HashMap<String, String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LogEntry {
pub timestamp: chrono::DateTime<chrono::Utc>,
pub status: u16,
pub method: String,
pub url: String,
pub response_time: u64,
pub size: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct HealthCheck {
pub status: String,
pub services: HashMap<String, String>,
pub last_check: chrono::DateTime<chrono::Utc>,
pub issues: Vec<String>,
}
impl HealthCheck {
pub fn healthy() -> Self {
Self {
status: "healthy".to_string(),
services: HashMap::new(),
last_check: chrono::Utc::now(),
issues: Vec::new(),
}
}
pub fn unhealthy(issues: Vec<String>) -> Self {
Self {
status: "unhealthy".to_string(),
services: HashMap::new(),
last_check: chrono::Utc::now(),
issues,
}
}
pub fn with_service(mut self, name: String, status: String) -> Self {
self.services.insert(name, status);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceSummary {
pub id: String,
pub name: String,
pub description: Option<String>,
pub active: bool,
pub folder_count: usize,
pub request_count: usize,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FolderSummary {
pub id: String,
pub name: String,
pub description: Option<String>,
pub parent_id: Option<String>,
pub subfolder_count: usize,
pub request_count: usize,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RequestSummary {
pub id: String,
pub name: String,
pub description: Option<String>,
pub method: String,
pub path: String,
pub status_code: u16,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceDetail {
pub summary: WorkspaceSummary,
pub folders: Vec<FolderSummary>,
pub requests: Vec<RequestSummary>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FolderDetail {
pub summary: FolderSummary,
pub subfolders: Vec<FolderSummary>,
pub requests: Vec<RequestSummary>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateWorkspaceRequest {
pub name: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateFolderRequest {
pub name: String,
pub description: Option<String>,
pub parent_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateRequestRequest {
pub name: String,
pub description: Option<String>,
pub method: String,
pub path: String,
pub status_code: Option<u16>,
pub response_body: Option<String>,
pub folder_id: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImportToWorkspaceRequest {
pub format: String,
pub data: String,
pub folder_id: Option<String>,
pub create_folders: Option<bool>,
pub selected_routes: Option<Vec<usize>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExportWorkspacesRequest {
pub workspace_ids: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WorkspaceExportData {
pub workspaces: Vec<mockforge_core::Workspace>,
pub version: String,
pub exported_at: chrono::DateTime<chrono::Utc>,
pub exporter_version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnvironmentColor {
pub hex: String,
pub name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnvironmentSummary {
pub id: String,
pub name: String,
pub description: Option<String>,
pub color: Option<EnvironmentColor>,
pub variable_count: usize,
pub active: bool,
pub is_global: bool,
pub created_at: chrono::DateTime<chrono::Utc>,
pub updated_at: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnvironmentVariable {
pub name: String,
pub value: String,
pub from_global: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateEnvironmentRequest {
pub name: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateEnvironmentRequest {
pub name: Option<String>,
pub description: Option<String>,
pub color: Option<EnvironmentColor>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SetVariableRequest {
pub name: String,
pub value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SyncConfig {
pub enabled: bool,
pub target_directory: Option<String>,
pub directory_structure: SyncDirectoryStructure,
pub sync_direction: SyncDirection,
pub include_metadata: bool,
pub realtime_monitoring: bool,
pub filename_pattern: String,
pub exclude_pattern: Option<String>,
pub force_overwrite: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SyncDirectoryStructure {
Flat,
Nested,
Grouped,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SyncDirection {
Manual,
WorkspaceToDirectory,
Bidirectional,
}
impl From<mockforge_core::workspace::SyncDirection> for SyncDirection {
fn from(core: mockforge_core::workspace::SyncDirection) -> Self {
match core {
mockforge_core::workspace::SyncDirection::Manual => Self::Manual,
mockforge_core::workspace::SyncDirection::WorkspaceToDirectory => {
Self::WorkspaceToDirectory
}
mockforge_core::workspace::SyncDirection::Bidirectional => Self::Bidirectional,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SyncStatus {
pub workspace_id: String,
pub enabled: bool,
pub target_directory: Option<String>,
pub sync_direction: SyncDirection,
pub realtime_monitoring: bool,
pub last_sync: Option<chrono::DateTime<chrono::Utc>>,
pub status: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SyncChange {
pub change_type: String,
pub path: String,
pub description: String,
pub requires_confirmation: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigureSyncRequest {
pub target_directory: String,
pub sync_direction: SyncDirection,
pub realtime_monitoring: bool,
pub directory_structure: Option<SyncDirectoryStructure>,
pub filename_pattern: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfirmSyncChangesRequest {
pub workspace_id: String,
pub changes: Vec<SyncChange>,
pub apply_all: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AutocompleteSuggestion {
pub text: String,
pub kind: String,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AutocompleteRequest {
pub input: String,
pub cursor_position: usize,
pub context: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AutocompleteResponse {
pub suggestions: Vec<AutocompleteSuggestion>,
pub start_position: usize,
pub end_position: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeSeriesPoint {
pub timestamp: chrono::DateTime<chrono::Utc>,
pub value: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TimeSeriesData {
pub points: Vec<TimeSeriesPoint>,
pub metric: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RestartStatus {
pub restarting: bool,
pub progress: f64,
pub message: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SmokeTestResult {
pub test_name: String,
pub passed: bool,
pub response_time_ms: Option<u64>,
pub error_message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SmokeTestContext {
pub suite_name: String,
pub total_tests: usize,
pub passed_tests: usize,
pub failed_tests: usize,
pub start_time: chrono::DateTime<chrono::Utc>,
pub end_time: Option<chrono::DateTime<chrono::Utc>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigurationState {
pub valid: bool,
pub errors: Vec<String>,
pub warnings: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImportHistoryEntry {
pub id: String,
pub format: String,
pub timestamp: chrono::DateTime<chrono::Utc>,
pub routes_count: usize,
pub variables_count: usize,
pub warnings_count: usize,
pub success: bool,
pub filename: Option<String>,
pub environment: Option<String>,
pub base_url: Option<String>,
pub error_message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FixtureInfo {
pub id: String,
pub name: String,
pub path: String,
pub size_bytes: u64,
pub last_modified: chrono::DateTime<chrono::Utc>,
pub content_type: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FixtureDeleteRequest {
pub fixture_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FixtureBulkDeleteRequest {
pub fixture_ids: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FixtureBulkDeleteResult {
pub deleted: Vec<String>,
pub failed: HashMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImportRoute {
pub method: String,
pub path: String,
pub headers: HashMap<String, String>,
pub body: Option<String>,
pub response: ImportResponse,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImportResponse {
pub status: u16,
pub headers: HashMap<String, String>,
pub body: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ImportResult {
pub routes: Vec<ImportRoute>,
pub warnings: Vec<String>,
pub errors: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InsomniaImportResult {
pub routes: Vec<ImportRoute>,
pub variables: HashMap<String, String>,
pub warnings: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerInfo {
pub version: String,
pub build_time: String,
pub git_sha: String,
pub http_server: Option<String>,
pub ws_server: Option<String>,
pub grpc_server: Option<String>,
pub graphql_server: Option<String>,
pub api_enabled: bool,
pub admin_port: u16,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_api_response_success() {
let data = "test data".to_string();
let response = ApiResponse::success(data.clone());
assert!(response.success);
assert_eq!(response.data, Some(data));
assert!(response.error.is_none());
}
#[test]
fn test_api_response_error() {
let error_msg = "Something went wrong".to_string();
let response: ApiResponse<String> = ApiResponse::error(error_msg.clone());
assert!(!response.success);
assert!(response.data.is_none());
assert_eq!(response.error, Some(error_msg));
}
#[test]
fn test_api_response_success_with_int() {
let response = ApiResponse::success(42);
assert!(response.success);
assert_eq!(response.data, Some(42));
}
#[test]
fn test_health_check_healthy() {
let health = HealthCheck::healthy();
assert_eq!(health.status, "healthy");
assert!(health.issues.is_empty());
assert!(health.services.is_empty());
}
#[test]
fn test_health_check_unhealthy() {
let issues = vec!["Database down".to_string(), "Cache error".to_string()];
let health = HealthCheck::unhealthy(issues.clone());
assert_eq!(health.status, "unhealthy");
assert_eq!(health.issues, issues);
}
#[test]
fn test_health_check_with_service() {
let health = HealthCheck::healthy()
.with_service("http".to_string(), "running".to_string())
.with_service("grpc".to_string(), "running".to_string());
assert_eq!(health.services.len(), 2);
assert_eq!(health.services.get("http"), Some(&"running".to_string()));
}
#[test]
fn test_log_filter_default() {
let filter = LogFilter::default();
assert!(filter.method.is_none());
assert!(filter.path_pattern.is_none());
assert_eq!(filter.hours_ago, Some(24));
assert_eq!(filter.limit, Some(100));
}
#[test]
fn test_log_filter_custom() {
let filter = LogFilter {
method: Some("POST".to_string()),
path_pattern: Some("/api/.*".to_string()),
status_code: Some(200),
hours_ago: Some(48),
limit: Some(500),
};
assert_eq!(filter.method.as_deref(), Some("POST"));
assert_eq!(filter.status_code, Some(200));
}
#[test]
fn test_server_status() {
let status = ServerStatus {
server_type: "HTTP".to_string(),
address: Some("127.0.0.1:3000".to_string()),
running: true,
start_time: Some(chrono::Utc::now()),
uptime_seconds: Some(3600),
active_connections: 10,
total_requests: 1000,
};
assert_eq!(status.server_type, "HTTP");
assert!(status.running);
assert_eq!(status.active_connections, 10);
}
#[test]
fn test_server_status_serialization() {
let status = ServerStatus {
server_type: "gRPC".to_string(),
address: None,
running: false,
start_time: None,
uptime_seconds: None,
active_connections: 0,
total_requests: 0,
};
let json = serde_json::to_string(&status).unwrap();
let deserialized: ServerStatus = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.server_type, "gRPC");
}
#[test]
fn test_route_info() {
let route = RouteInfo {
method: Some("GET".to_string()),
path: "/api/users".to_string(),
priority: 100,
has_fixtures: true,
latency_ms: Some(50),
request_count: 500,
last_request: None,
error_count: 5,
};
assert_eq!(route.method, Some("GET".to_string()));
assert_eq!(route.path, "/api/users");
assert!(route.has_fixtures);
}
#[test]
fn test_route_info_clone() {
let route = RouteInfo {
method: Some("POST".to_string()),
path: "/api/items".to_string(),
priority: 50,
has_fixtures: false,
latency_ms: None,
request_count: 0,
last_request: None,
error_count: 0,
};
let cloned = route.clone();
assert_eq!(cloned.path, route.path);
assert_eq!(cloned.priority, route.priority);
}
#[test]
fn test_request_log() {
let log = RequestLog {
id: "req-123".to_string(),
timestamp: chrono::Utc::now(),
method: "GET".to_string(),
path: "/api/data".to_string(),
status_code: 200,
response_time_ms: 45,
client_ip: Some("192.168.1.1".to_string()),
user_agent: Some("curl/7.64.0".to_string()),
headers: HashMap::new(),
response_size_bytes: 1024,
error_message: None,
};
assert_eq!(log.id, "req-123");
assert_eq!(log.status_code, 200);
}
#[test]
fn test_system_info() {
let info = SystemInfo {
version: "0.3.8".to_string(),
uptime_seconds: 86400,
memory_usage_mb: 256,
cpu_usage_percent: 25.5,
active_threads: 8,
total_routes: 50,
total_fixtures: 100,
};
assert_eq!(info.version, "0.3.8");
assert_eq!(info.total_routes, 50);
}
#[test]
fn test_latency_profile() {
let profile = LatencyProfile {
name: "slow_network".to_string(),
base_ms: 200,
jitter_ms: 50,
tag_overrides: HashMap::from([("critical".to_string(), 50)]),
};
assert_eq!(profile.name, "slow_network");
assert_eq!(profile.base_ms, 200);
}
#[test]
fn test_fault_config() {
let config = FaultConfig {
enabled: true,
failure_rate: 0.1,
status_codes: vec![500, 503],
active_failures: 5,
};
assert!(config.enabled);
assert!(config.failure_rate > 0.0);
assert_eq!(config.status_codes.len(), 2);
}
#[test]
fn test_proxy_config() {
let config = ProxyConfig {
enabled: true,
upstream_url: Some("https://api.example.com".to_string()),
timeout_seconds: 30,
requests_proxied: 1000,
};
assert!(config.enabled);
assert!(config.upstream_url.is_some());
}
#[test]
fn test_bandwidth_config() {
let config = BandwidthConfig {
enabled: true,
max_bytes_per_sec: 1_000_000,
burst_capacity_bytes: 10_000,
tag_overrides: HashMap::new(),
};
assert!(config.enabled);
assert_eq!(config.max_bytes_per_sec, 1_000_000);
}
#[test]
fn test_burst_loss_config() {
let config = BurstLossConfig {
enabled: false,
burst_probability: 0.05,
burst_duration_ms: 1000,
loss_rate_during_burst: 0.3,
recovery_time_ms: 5000,
tag_overrides: HashMap::new(),
};
assert!(!config.enabled);
assert_eq!(config.burst_duration_ms, 1000);
}
#[test]
fn test_simple_metrics_data() {
let metrics = SimpleMetricsData {
total_requests: 10000,
active_requests: 5,
average_response_time: 45.5,
error_rate: 0.02,
};
assert_eq!(metrics.total_requests, 10000);
assert!(metrics.error_rate < 0.05);
}
#[test]
fn test_config_update() {
let update = ConfigUpdate {
config_type: "latency".to_string(),
data: serde_json::json!({"base_ms": 100}),
};
assert_eq!(update.config_type, "latency");
}
#[test]
fn test_route_update() {
let update = RouteUpdate {
path: "/api/users".to_string(),
method: Some("POST".to_string()),
operation: "create".to_string(),
data: Some(serde_json::json!({"fixture": "user.json"})),
};
assert_eq!(update.path, "/api/users");
assert_eq!(update.operation, "create");
}
#[test]
fn test_validation_settings() {
let settings = ValidationSettings {
mode: "enforce".to_string(),
aggregate_errors: true,
validate_responses: true,
overrides: HashMap::new(),
};
assert_eq!(settings.mode, "enforce");
assert!(settings.validate_responses);
}
#[test]
fn test_log_entry() {
let entry = LogEntry {
timestamp: chrono::Utc::now(),
status: 200,
method: "GET".to_string(),
url: "/api/test".to_string(),
response_time: 50,
size: 1024,
};
assert_eq!(entry.status, 200);
assert_eq!(entry.method, "GET");
}
#[test]
fn test_workspace_summary() {
let summary = WorkspaceSummary {
id: "ws-123".to_string(),
name: "My Workspace".to_string(),
description: Some("Test workspace".to_string()),
active: true,
folder_count: 5,
request_count: 20,
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
};
assert_eq!(summary.name, "My Workspace");
assert!(summary.active);
}
#[test]
fn test_folder_summary() {
let folder = FolderSummary {
id: "folder-123".to_string(),
name: "API Tests".to_string(),
description: None,
parent_id: None,
subfolder_count: 3,
request_count: 10,
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
};
assert_eq!(folder.name, "API Tests");
assert!(folder.parent_id.is_none());
}
#[test]
fn test_request_summary() {
let request = RequestSummary {
id: "req-123".to_string(),
name: "Get Users".to_string(),
description: Some("Fetch all users".to_string()),
method: "GET".to_string(),
path: "/api/users".to_string(),
status_code: 200,
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
};
assert_eq!(request.method, "GET");
assert_eq!(request.status_code, 200);
}
#[test]
fn test_create_workspace_request() {
let request = CreateWorkspaceRequest {
name: "New Workspace".to_string(),
description: Some("A new workspace".to_string()),
};
assert_eq!(request.name, "New Workspace");
}
#[test]
fn test_create_folder_request() {
let request = CreateFolderRequest {
name: "New Folder".to_string(),
description: None,
parent_id: Some("parent-123".to_string()),
};
assert_eq!(request.name, "New Folder");
assert!(request.parent_id.is_some());
}
#[test]
fn test_sync_direction_conversion() {
let manual = mockforge_core::workspace::SyncDirection::Manual;
let ui_manual: SyncDirection = manual.into();
assert!(matches!(ui_manual, SyncDirection::Manual));
}
#[test]
fn test_sync_direction_workspace_to_directory() {
let dir = mockforge_core::workspace::SyncDirection::WorkspaceToDirectory;
let ui_dir: SyncDirection = dir.into();
assert!(matches!(ui_dir, SyncDirection::WorkspaceToDirectory));
}
#[test]
fn test_sync_direction_bidirectional() {
let bidir = mockforge_core::workspace::SyncDirection::Bidirectional;
let ui_bidir: SyncDirection = bidir.into();
assert!(matches!(ui_bidir, SyncDirection::Bidirectional));
}
#[test]
fn test_environment_color() {
let color = EnvironmentColor {
hex: "#FF5733".to_string(),
name: Some("Orange".to_string()),
};
assert_eq!(color.hex, "#FF5733");
assert_eq!(color.name, Some("Orange".to_string()));
}
#[test]
fn test_environment_color_without_name() {
let color = EnvironmentColor {
hex: "#00FF00".to_string(),
name: None,
};
assert_eq!(color.hex, "#00FF00");
assert!(color.name.is_none());
}
#[test]
fn test_environment_summary() {
let env = EnvironmentSummary {
id: "env-123".to_string(),
name: "Production".to_string(),
description: Some("Production environment".to_string()),
color: Some(EnvironmentColor {
hex: "#FF0000".to_string(),
name: Some("Red".to_string()),
}),
variable_count: 10,
active: true,
is_global: false,
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
};
assert_eq!(env.name, "Production");
assert!(env.active);
assert!(!env.is_global);
}
#[test]
fn test_autocomplete_suggestion() {
let suggestion = AutocompleteSuggestion {
text: "{{base_url}}".to_string(),
kind: "variable".to_string(),
description: Some("Base URL variable".to_string()),
};
assert_eq!(suggestion.kind, "variable");
}
#[test]
fn test_time_series_point() {
let point = TimeSeriesPoint {
timestamp: chrono::Utc::now(),
value: 123.45,
};
assert!((point.value - 123.45).abs() < f64::EPSILON);
}
#[test]
fn test_restart_status() {
let status = RestartStatus {
restarting: true,
progress: 0.5,
message: "Restarting services...".to_string(),
};
assert!(status.restarting);
assert!((status.progress - 0.5).abs() < f64::EPSILON);
}
#[test]
fn test_smoke_test_result_passed() {
let result = SmokeTestResult {
test_name: "Health Check".to_string(),
passed: true,
response_time_ms: Some(50),
error_message: None,
};
assert!(result.passed);
assert!(result.error_message.is_none());
}
#[test]
fn test_smoke_test_result_failed() {
let result = SmokeTestResult {
test_name: "Database Connection".to_string(),
passed: false,
response_time_ms: None,
error_message: Some("Connection timeout".to_string()),
};
assert!(!result.passed);
assert!(result.error_message.is_some());
}
#[test]
fn test_configuration_state_valid() {
let state = ConfigurationState {
valid: true,
errors: vec![],
warnings: vec!["Deprecated setting used".to_string()],
};
assert!(state.valid);
assert!(state.errors.is_empty());
assert!(!state.warnings.is_empty());
}
#[test]
fn test_import_history_entry() {
let entry = ImportHistoryEntry {
id: "import-123".to_string(),
format: "postman".to_string(),
timestamp: chrono::Utc::now(),
routes_count: 25,
variables_count: 5,
warnings_count: 2,
success: true,
filename: Some("collection.json".to_string()),
environment: Some("development".to_string()),
base_url: Some("https://api.example.com".to_string()),
error_message: None,
};
assert!(entry.success);
assert_eq!(entry.routes_count, 25);
}
#[test]
fn test_fixture_info() {
let fixture = FixtureInfo {
id: "fixture-123".to_string(),
name: "users.json".to_string(),
path: "/fixtures/users.json".to_string(),
size_bytes: 2048,
last_modified: chrono::Utc::now(),
content_type: Some("application/json".to_string()),
};
assert_eq!(fixture.name, "users.json");
assert_eq!(fixture.size_bytes, 2048);
}
#[test]
fn test_import_route() {
let route = ImportRoute {
method: "POST".to_string(),
path: "/api/users".to_string(),
headers: HashMap::from([("Content-Type".to_string(), "application/json".to_string())]),
body: Some(r#"{"name": "test"}"#.to_string()),
response: ImportResponse {
status: 201,
headers: HashMap::new(),
body: serde_json::json!({"id": 1}),
},
};
assert_eq!(route.method, "POST");
assert_eq!(route.response.status, 201);
}
#[test]
fn test_server_info() {
let info = ServerInfo {
version: "0.3.8".to_string(),
build_time: "2024-01-01T00:00:00Z".to_string(),
git_sha: "abc123".to_string(),
http_server: Some("0.0.0.0:3000".to_string()),
ws_server: Some("0.0.0.0:3001".to_string()),
grpc_server: None,
graphql_server: None,
api_enabled: true,
admin_port: 8080,
};
assert_eq!(info.version, "0.3.8");
assert!(info.api_enabled);
}
#[test]
fn test_log_filter_serialization() {
let filter = LogFilter::default();
let json = serde_json::to_string(&filter).unwrap();
let deserialized: LogFilter = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.hours_ago, filter.hours_ago);
}
#[test]
fn test_health_check_serialization() {
let health = HealthCheck::healthy().with_service("api".to_string(), "ok".to_string());
let json = serde_json::to_string(&health).unwrap();
let deserialized: HealthCheck = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.status, "healthy");
}
}