1pub mod buffer_pool;
2pub mod config;
3pub mod errors;
4pub mod http_client;
5pub mod metrics;
6pub mod parser_cache;
7pub mod pool;
8pub mod response;
9pub mod retry;
10
11#[cfg(feature = "client")]
12pub mod ipc_http_client;
13
14#[cfg(feature = "client")]
15pub mod ipc_stream_client;
16
17#[cfg(feature = "client")]
18pub mod stream_client;
19
20#[cfg(feature = "server")]
21pub mod ipc_http_server;
22
23#[cfg(feature = "server")]
24pub mod ipc_stream_server;
25
26pub use config::*;
27pub use errors::*;
28pub use metrics::{
29 global_metrics, init_metrics, BufferPoolStats, HealthChecker, HealthReport, HealthStatus,
30 MetricsCollector, MetricsSnapshot, ParserCacheStats,
31};
32pub use response::*;
33
34#[cfg(feature = "client")]
35pub use ipc_http_client::*;
36
37#[cfg(feature = "client")]
38pub use ipc_stream_client::*;
39
40#[cfg(feature = "client")]
41pub use stream_client::*;
42
43#[cfg(feature = "server")]
44pub use ipc_http_server::{IpcHttpServer, RequestContext, Router, ServerConfig};
45
46#[cfg(feature = "server")]
47pub use ipc_stream_server::*;
48
49#[cfg(test)]
50mod test_utils {
51 use crate::config::GlobalConfig;
52 use tokio::sync::oneshot;
53
54 pub fn test_config() -> GlobalConfig {
55 let mut config = GlobalConfig::default();
56 config.client.default_timeout_ms = 5000;
57 config.client.max_retries = 2;
58 config.client.retry_delay_ms = 100;
59 config.client.connection_timeout_ms = 1000;
60 config.client.pool.max_size = 5;
61 config
62 }
63
64 pub async fn _setup_test_server(
65 _socket_path: &str,
66 ) -> (tokio::task::JoinHandle<()>, oneshot::Sender<()>) {
67 let (shutdown_tx, _shutdown_rx) = oneshot::channel();
69 let handle = tokio::spawn(async move {
70 });
72 (handle, shutdown_tx)
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use test_utils::*;
80
81 #[test]
82 fn test_config_validation() {
83 let mut invalid_config = test_config();
84 invalid_config.client.default_timeout_ms = 0;
85
86 assert!(invalid_config.validate().is_err());
87
88 let valid_config = test_config();
89 assert!(valid_config.validate().is_ok());
90 }
91
92 #[test]
93 fn test_config_builder() {
94 use crate::config::ConfigBuilder;
95 use std::time::Duration;
96
97 let config = ConfigBuilder::new()
98 .client_timeout(Duration::from_millis(3000))
99 .max_retries(3)
100 .enable_logging("debug")
101 .enable_feature("caching")
102 .build()
103 .unwrap();
104
105 assert_eq!(config.client.default_timeout_ms, 3000);
106 assert_eq!(config.client.max_retries, 3);
107 assert_eq!(config.logging.level, "debug");
108 assert!(config.features.caching);
109 }
110
111 #[test]
112 fn test_error_categorization() {
113 use crate::errors::KodeBridgeError;
114
115 let connection_error = KodeBridgeError::Connection {
116 message: "Connection failed".to_string(),
117 };
118 assert!(connection_error.is_retriable());
119 assert!(!connection_error.is_client_error());
120 assert!(!connection_error.is_server_error());
121
122 let config_error = KodeBridgeError::Configuration {
123 message: "Invalid configuration".to_string(),
124 };
125 assert!(!config_error.is_retriable());
126
127 let client_error = KodeBridgeError::ClientError { status: 400 };
128 assert!(client_error.is_client_error());
129 assert!(!client_error.is_server_error());
130
131 let server_error = KodeBridgeError::ServerError { status: 500 };
132 assert!(server_error.is_server_error());
133 assert!(!server_error.is_client_error());
134 }
135
136 #[test]
137 fn test_timeout_error() {
138 use crate::errors::KodeBridgeError;
139
140 let timeout_error = KodeBridgeError::timeout(5000);
141 assert!(timeout_error.is_retriable());
142
143 match timeout_error {
144 KodeBridgeError::Timeout { duration_ms } => {
145 assert_eq!(duration_ms, 5000);
146 }
147 _ => panic!("Expected timeout error"),
148 }
149 }
150
151 #[test]
152 fn test_error_construction_helpers() {
153 use crate::errors::KodeBridgeError;
154
155 let conn_err = KodeBridgeError::connection("Test connection error");
156 assert!(
157 matches!(conn_err, KodeBridgeError::Connection { message } if message == "Test connection error")
158 );
159
160 let proto_err = KodeBridgeError::protocol("Protocol violation");
161 assert!(
162 matches!(proto_err, KodeBridgeError::Protocol { message } if message == "Protocol violation")
163 );
164
165 let config_err = KodeBridgeError::configuration("Bad config");
166 assert!(
167 matches!(config_err, KodeBridgeError::Configuration { message } if message == "Bad config")
168 );
169
170 let custom_err = KodeBridgeError::custom("Custom error message");
171 assert!(
172 matches!(custom_err, KodeBridgeError::Custom { message } if message == "Custom error message")
173 );
174 }
175
176 #[test]
177 fn test_pool_config() {
178 use crate::pool::PoolConfig;
179
180 let pool_config = PoolConfig {
181 max_size: 10,
182 min_idle: 2,
183 max_idle_time_ms: 300_000,
184 connection_timeout_ms: 30_000,
185 retry_delay_ms: 100,
186 max_retries: 3,
187 max_concurrent_requests: 8,
188 max_requests_per_second: Some(10.0),
189 };
190
191 assert_eq!(pool_config.max_size, 10);
192 assert_eq!(pool_config.min_idle, 2);
193 assert_eq!(pool_config.max_idle_time_ms, 300_000);
194
195 let default_config = PoolConfig::default();
196 assert_eq!(default_config.max_size, 20);
197 assert_eq!(default_config.min_idle, 5);
198 }
199
200 #[cfg(feature = "server")]
201 #[test]
202 fn test_path_security() {
203 use crate::ipc_http_server::Router;
204
205 let router = Router::new();
206
207 assert!(router.is_safe_path("/api/users"));
209 assert!(router.is_safe_path("/"));
210 assert!(router.is_safe_path("/data/file.json"));
211
212 assert!(!router.is_safe_path("/../etc/passwd"));
214 assert!(!router.is_safe_path("/api/../../../etc/passwd"));
215 assert!(!router.is_safe_path("/data\\..\\windows"));
216
217 assert!(!router.is_safe_path("/api/users\0"));
219 assert!(!router.is_safe_path("/api/\x01users"));
220
221 assert!(!router.is_safe_path("api/users"));
223 assert!(!router.is_safe_path("../etc/passwd"));
224
225 let long_path = "/".to_string() + &"a".repeat(3000);
227 assert!(!router.is_safe_path(&long_path));
228 }
229
230 #[cfg(feature = "server")]
231 #[test]
232 fn test_url_decoding_security() {
233 use crate::ipc_http_server::urlencoding;
234
235 assert_eq!(urlencoding::decode("hello%20world").unwrap(), "hello world");
237 assert_eq!(urlencoding::decode("hello+world").unwrap(), "hello world");
238 assert_eq!(urlencoding::decode("hello").unwrap(), "hello");
239
240 assert!(urlencoding::decode("hello%GG").is_err());
242 assert!(urlencoding::decode("hello%2").is_err());
243
244 assert!(urlencoding::decode("%C0%80").is_err());
247
248 assert_eq!(urlencoding::decode("%C3%A9").unwrap(), "é"); }
251
252 #[test]
253 fn test_metrics_integration() {
254 use crate::metrics::global_metrics;
255
256 let metrics = global_metrics();
257
258 {
260 let tracker = metrics.request_start("GET");
261 std::thread::sleep(std::time::Duration::from_millis(1));
262 tracker.success(200);
263 }
264
265 let snapshot = metrics.snapshot();
266 assert_eq!(snapshot.total_requests, 1);
267 assert_eq!(snapshot.successful_requests, 1);
268 assert_eq!(snapshot.active_requests, 0);
269
270 metrics.connection_created(true);
272 metrics.connection_created(false);
273
274 let snapshot = metrics.snapshot();
275 assert_eq!(snapshot.total_connections, 2);
276 assert_eq!(snapshot.pool_hits, 1);
277 assert_eq!(snapshot.pool_misses, 1);
278 }
279
280 #[test]
281 fn test_health_checker() {
282 use crate::metrics::{HealthChecker, HealthStatus, MetricsCollector};
283 use std::sync::Arc;
284
285 let metrics = Arc::new(MetricsCollector::new());
286 let health_checker = HealthChecker::new(metrics);
287
288 let report = health_checker.check_health();
289 assert_eq!(report.status, HealthStatus::Healthy);
290 assert!(report.issues.is_empty());
291 }
292
293 #[test]
294 fn test_buffer_pool_integration() {
295 use crate::buffer_pool::global_pools;
296 use crate::metrics::global_metrics;
297
298 let pools = global_pools();
299 let metrics = global_metrics();
300
301 let _buf1 = pools.get_small();
303 let _buf2 = pools.get_medium();
304 let _buf3 = pools.get_large();
305
306 let stats = pools.stats();
307 metrics.update_buffer_pool_stats(crate::metrics::BufferPoolStats {
308 small_pool_size: stats.small_pool_size,
309 medium_pool_size: stats.medium_pool_size,
310 large_pool_size: stats.large_pool_size,
311 total_allocations: 3,
312 total_reuses: 0,
313 });
314
315 let snapshot = metrics.snapshot();
316 assert_eq!(snapshot.buffer_pool_stats.total_allocations, 3);
317 }
318}