Skip to main content

mockforge_tunnel/
lib.rs

1//! MockForge Tunneling Service
2//!
3//! Provides functionality to expose local MockForge servers via public URLs
4//! without requiring cloud deployment. Supports multiple tunneling backends
5//! and provides a unified API for tunnel management.
6
7#[cfg(feature = "server")]
8pub mod audit;
9pub mod client;
10pub mod config;
11pub mod manager;
12pub mod provider;
13#[cfg(feature = "server")]
14pub mod rate_limit;
15#[cfg(feature = "server")]
16pub mod server;
17#[cfg(feature = "server")]
18pub mod server_config;
19#[cfg(feature = "server")]
20pub mod storage;
21
22pub use client::TunnelClient;
23pub use config::{TunnelConfig, TunnelProvider};
24pub use manager::TunnelManager;
25pub use provider::{TunnelProvider as ProviderTrait, TunnelStatus};
26
27/// Result type for tunnel operations
28pub type Result<T> = std::result::Result<T, TunnelError>;
29
30/// Error type for tunnel operations
31#[derive(Debug, thiserror::Error)]
32pub enum TunnelError {
33    #[error("Tunnel connection failed: {0}")]
34    ConnectionFailed(String),
35
36    #[error("Tunnel provider error: {0}")]
37    ProviderError(String),
38
39    #[error("Configuration error: {0}")]
40    ConfigError(String),
41
42    #[error("Tunnel not found: {0}")]
43    NotFound(String),
44
45    #[error("Tunnel already exists: {0}")]
46    AlreadyExists(String),
47
48    #[error("HTTP error: {0}")]
49    Http(#[from] reqwest::Error),
50
51    #[error("URL parsing error: {0}")]
52    Url(#[from] url::ParseError),
53
54    #[error("Serialization error: {0}")]
55    Serialization(#[from] serde_json::Error),
56
57    #[error("IO error: {0}")]
58    Io(#[from] std::io::Error),
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64
65    #[test]
66    fn test_tunnel_error_connection_failed() {
67        let error = TunnelError::ConnectionFailed("connection refused".to_string());
68        let display = error.to_string();
69        assert!(display.contains("Tunnel connection failed"));
70        assert!(display.contains("connection refused"));
71    }
72
73    #[test]
74    fn test_tunnel_error_provider_error() {
75        let error = TunnelError::ProviderError("provider unavailable".to_string());
76        let display = error.to_string();
77        assert!(display.contains("Tunnel provider error"));
78        assert!(display.contains("provider unavailable"));
79    }
80
81    #[test]
82    fn test_tunnel_error_config_error() {
83        let error = TunnelError::ConfigError("missing server_url".to_string());
84        let display = error.to_string();
85        assert!(display.contains("Configuration error"));
86        assert!(display.contains("missing server_url"));
87    }
88
89    #[test]
90    fn test_tunnel_error_not_found() {
91        let error = TunnelError::NotFound("tunnel-123".to_string());
92        let display = error.to_string();
93        assert!(display.contains("Tunnel not found"));
94        assert!(display.contains("tunnel-123"));
95    }
96
97    #[test]
98    fn test_tunnel_error_already_exists() {
99        let error = TunnelError::AlreadyExists("tunnel-456".to_string());
100        let display = error.to_string();
101        assert!(display.contains("Tunnel already exists"));
102        assert!(display.contains("tunnel-456"));
103    }
104
105    #[test]
106    fn test_tunnel_error_debug() {
107        let error = TunnelError::ConnectionFailed("test".to_string());
108        let debug = format!("{:?}", error);
109        assert!(debug.contains("ConnectionFailed"));
110        assert!(debug.contains("test"));
111    }
112
113    #[test]
114    fn test_tunnel_error_from_url_parse() {
115        let url_error = url::ParseError::EmptyHost;
116        let error: TunnelError = url_error.into();
117        let display = error.to_string();
118        assert!(display.contains("URL parsing error"));
119    }
120
121    #[test]
122    fn test_tunnel_error_from_json() {
123        let json_str = "invalid json";
124        let json_error = serde_json::from_str::<serde_json::Value>(json_str).unwrap_err();
125        let error: TunnelError = json_error.into();
126        let display = error.to_string();
127        assert!(display.contains("Serialization error"));
128    }
129
130    #[test]
131    fn test_tunnel_error_from_io() {
132        let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
133        let error: TunnelError = io_error.into();
134        let display = error.to_string();
135        assert!(display.contains("IO error"));
136        assert!(display.contains("file not found"));
137    }
138
139    #[test]
140    fn test_result_type_ok() {
141        let result: Result<i32> = Ok(42);
142        assert_eq!(result.unwrap(), 42);
143    }
144
145    #[test]
146    fn test_result_type_err() {
147        let result: Result<i32> = Err(TunnelError::NotFound("tunnel".to_string()));
148        assert!(result.is_err());
149    }
150
151    #[test]
152    fn test_tunnel_error_variants_are_send_sync() {
153        fn assert_send_sync<T: Send + Sync>() {}
154        // TunnelError should be Send + Sync for use across threads
155        // Note: This is a compile-time check
156        // assert_send_sync::<TunnelError>(); // Would fail due to reqwest::Error
157    }
158
159    #[test]
160    fn test_tunnel_error_connection_failed_empty_message() {
161        let error = TunnelError::ConnectionFailed(String::new());
162        let display = error.to_string();
163        assert!(display.contains("Tunnel connection failed"));
164    }
165
166    #[test]
167    fn test_tunnel_error_chained_display() {
168        let io_error = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
169        let error: TunnelError = io_error.into();
170        // Check that the error chain is preserved
171        let display = error.to_string();
172        assert!(display.contains("access denied"));
173    }
174}