1#[cfg(feature = "server")]
2pub mod codec;
3pub mod config;
4pub mod errors;
5pub mod http_client;
6pub mod metrics;
7pub mod parser_cache;
8pub mod pool;
9pub mod response;
10pub mod retry;
11mod transport;
12
13pub use transport::{Endpoint, IpcStream, ListenerOptions};
14
15#[cfg(feature = "client")]
16pub mod ipc_http_client;
17
18#[cfg(feature = "client")]
19pub mod ipc_stream_client;
20
21#[cfg(feature = "client")]
22pub mod stream_client;
23
24#[cfg(feature = "server")]
25pub mod ipc_http_server;
26
27#[cfg(feature = "server")]
28pub mod ipc_stream_server;
29
30pub use config::*;
31pub use errors::*;
32pub use metrics::{
33 global_metrics, init_metrics, BufferPoolStats, HealthChecker, HealthReport, HealthStatus, MetricsCollector,
34 MetricsSnapshot, ParserCacheStats,
35};
36pub use response::*;
37
38#[cfg(feature = "client")]
39pub use ipc_http_client::*;
40
41#[cfg(feature = "client")]
42pub use ipc_stream_client::*;
43
44#[cfg(feature = "client")]
45pub use stream_client::*;
46
47#[cfg(feature = "server")]
48pub use ipc_http_server::{ClientInfo, IpcHttpServer, PeerCredentials, RequestContext, Router, ServerConfig};
49
50#[cfg(feature = "server")]
51pub use ipc_stream_server::*;
52
53#[cfg(test)]
54mod test_utils {
55 use crate::config::GlobalConfig;
56 use tokio::sync::oneshot;
57
58 pub fn test_config() -> GlobalConfig {
59 let mut config = GlobalConfig::default();
60 config.client.default_timeout_ms = 5000;
61 config.client.max_retries = 2;
62 config.client.retry_delay_ms = 100;
63 config.client.connection_timeout_ms = 1000;
64 config.client.pool.max_size = 5;
65 config.client.pool.min_idle = 2; config
67 }
68
69 pub fn _setup_test_server(_socket_path: &str) -> (tokio::task::JoinHandle<()>, oneshot::Sender<()>) {
70 let (shutdown_tx, _shutdown_rx) = oneshot::channel();
72 let handle = tokio::spawn(async move {
73 });
75 (handle, shutdown_tx)
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use test_utils::*;
83
84 #[test]
85 fn test_config_validation() {
86 let mut invalid_config = test_config();
87 invalid_config.client.default_timeout_ms = 0;
88
89 assert!(invalid_config.validate().is_err());
90
91 let valid_config = test_config();
92 assert!(valid_config.validate().is_ok());
93 }
94
95 #[test]
96 fn test_config_builder() {
97 use crate::config::ConfigBuilder;
98 use std::time::Duration;
99
100 let config = ConfigBuilder::new()
101 .client_timeout(Duration::from_millis(3000))
102 .max_retries(3)
103 .enable_logging("debug")
104 .enable_feature("caching")
105 .build()
106 .unwrap();
107
108 assert_eq!(config.client.default_timeout_ms, 3000);
109 assert_eq!(config.client.max_retries, 3);
110 assert_eq!(config.logging.level, "debug");
111 assert!(config.features.caching);
112 }
113
114 #[test]
115 fn test_error_categorization() {
116 use crate::errors::KodeBridgeError;
117
118 let connection_error = KodeBridgeError::Connection {
119 message: "Connection failed".to_string(),
120 };
121 assert!(connection_error.is_retriable());
122 assert!(!connection_error.is_client_error());
123 assert!(!connection_error.is_server_error());
124
125 let config_error = KodeBridgeError::Configuration {
126 message: "Invalid configuration".to_string(),
127 };
128 assert!(!config_error.is_retriable());
129
130 let client_error = KodeBridgeError::ClientError { status: 400 };
131 assert!(client_error.is_client_error());
132 assert!(!client_error.is_server_error());
133
134 let server_error = KodeBridgeError::ServerError { status: 500 };
135 assert!(server_error.is_server_error());
136 assert!(!server_error.is_client_error());
137 }
138
139 #[test]
140 fn test_timeout_error() {
141 use crate::errors::KodeBridgeError;
142
143 let timeout_error = KodeBridgeError::timeout(5000);
144 assert!(timeout_error.is_retriable());
145
146 assert!(matches!(timeout_error, KodeBridgeError::Timeout { duration_ms } if duration_ms == 5000));
147 }
148
149 #[test]
150 fn test_error_construction_helpers() {
151 use crate::errors::KodeBridgeError;
152
153 let conn_err = KodeBridgeError::connection("Test connection error");
154 assert!(matches!(conn_err, KodeBridgeError::Connection { message } if message == "Test connection error"));
155
156 let proto_err = KodeBridgeError::protocol("Protocol violation");
157 assert!(matches!(proto_err, KodeBridgeError::Protocol { message } if message == "Protocol violation"));
158
159 let config_err = KodeBridgeError::configuration("Bad config");
160 assert!(matches!(config_err, KodeBridgeError::Configuration { message } if message == "Bad config"));
161
162 let custom_err = KodeBridgeError::custom("Custom error message");
163 assert!(matches!(custom_err, KodeBridgeError::Custom { message } if message == "Custom error message"));
164 }
165
166 #[test]
167 fn test_pool_config() {
168 use crate::pool::PoolConfig;
169
170 let pool_config = PoolConfig {
171 max_size: 10,
172 min_idle: 2,
173 max_idle_time_ms: 300_000,
174 connection_timeout_ms: 30_000,
175 retry_delay_ms: 100,
176 max_retries: 3,
177 max_concurrent_requests: 8,
178 max_requests_per_second: Some(10.0),
179 };
180
181 assert_eq!(pool_config.max_size, 10);
182 assert_eq!(pool_config.min_idle, 2);
183 assert_eq!(pool_config.max_idle_time_ms, 300_000);
184
185 let default_config = PoolConfig::default();
186 assert_eq!(default_config.max_size, 64); assert_eq!(default_config.min_idle, 8); }
189
190 #[cfg(feature = "server")]
191 #[test]
192 fn test_path_security() {
193 use crate::ipc_http_server::Router;
194
195 let router = Router::new();
196
197 assert!(router.is_safe_path("/api/users"));
199 assert!(router.is_safe_path("/"));
200 assert!(router.is_safe_path("/data/file.json"));
201
202 assert!(!router.is_safe_path("/../etc/passwd"));
204 assert!(!router.is_safe_path("/api/../../../etc/passwd"));
205 assert!(!router.is_safe_path("/data\\..\\windows"));
206
207 assert!(!router.is_safe_path("/api/users\0"));
209 assert!(!router.is_safe_path("/api/\x01users"));
210
211 assert!(!router.is_safe_path("api/users"));
213 assert!(!router.is_safe_path("../etc/passwd"));
214
215 let long_path = "/".to_string() + &"a".repeat(3000);
217 assert!(!router.is_safe_path(&long_path));
218 }
219
220 #[test]
221 fn test_metrics_integration() {
222 use crate::metrics::global_metrics;
223
224 let metrics = global_metrics();
225
226 {
228 let tracker = metrics.request_start("GET");
229 std::thread::sleep(std::time::Duration::from_millis(1));
230 tracker.success(200);
231 }
232
233 let snapshot = metrics.snapshot();
234 assert_eq!(snapshot.total_requests, 1);
235 assert_eq!(snapshot.successful_requests, 1);
236 assert_eq!(snapshot.active_requests, 0);
237
238 metrics.connection_created(true);
240 metrics.connection_created(false);
241
242 let snapshot = metrics.snapshot();
243 assert_eq!(snapshot.total_connections, 2);
244 assert_eq!(snapshot.pool_hits, 1);
245 assert_eq!(snapshot.pool_misses, 1);
246 }
247
248 #[test]
249 fn test_health_checker() {
250 use crate::metrics::{HealthChecker, HealthStatus, MetricsCollector};
251 use std::sync::Arc;
252
253 let metrics = Arc::new(MetricsCollector::new());
254 let health_checker = HealthChecker::new(metrics);
255
256 let report = health_checker.check_health();
257 assert_eq!(report.status, HealthStatus::Healthy);
258 assert!(report.issues.is_empty());
259 }
260}