@@ -216,4 +216,182 @@ mod tests {
216
216
let mut rx = forward. shutdown . subscribe ( ) ;
217
217
assert ! ( rx. try_recv( ) . is_err( ) ) ; // Should be empty after stop
218
218
}
219
+
220
+ // This tests specifically depends on kind
221
+ // just a reminder in case it fails later on
222
+ #[ tokio:: test]
223
+ async fn test_establish_forward ( ) {
224
+ let config = ForwardConfig {
225
+ name : "test-forward" . to_string ( ) ,
226
+ target : "test-target.test-namespace" . to_string ( ) ,
227
+ ports : kube_forward:: config:: PortMapping {
228
+ local : 0 , // Use port 0 to let OS assign random port
229
+ remote : 80 ,
230
+ } ,
231
+ pod_selector : PodSelector {
232
+ label : None ,
233
+ annotation : None ,
234
+ } ,
235
+ local_dns : kube_forward:: config:: LocalDnsConfig {
236
+ enabled : false ,
237
+ hostname : None ,
238
+ } ,
239
+ options : kube_forward:: config:: ForwardOptions {
240
+ max_retries : 3 ,
241
+ retry_interval : Duration :: from_secs ( 1 ) ,
242
+ health_check_interval : Duration :: from_secs ( 5 ) ,
243
+ } ,
244
+ } ;
245
+
246
+ let service_info = ServiceInfo {
247
+ name : "kube-dns" . to_string ( ) ,
248
+ namespace : "kube-system" . to_string ( ) ,
249
+ ports : vec ! [ 53 ] ,
250
+ } ;
251
+
252
+ let forward = PortForward :: new ( config, service_info) ;
253
+ let client = kube:: Client :: try_default ( ) . await . unwrap ( ) ;
254
+
255
+ // Test port already in use
256
+ let listener = TcpListener :: bind ( "127.0.0.1:0" ) . await . unwrap ( ) ;
257
+ let port = listener. local_addr ( ) . unwrap ( ) . port ( ) ;
258
+
259
+ let mut config_with_used_port = forward. config . clone ( ) ;
260
+ config_with_used_port. ports . local = port;
261
+ let forward_with_used_port =
262
+ PortForward :: new ( config_with_used_port, forward. service_info . clone ( ) ) ;
263
+
264
+ let result = forward_with_used_port. establish_forward ( & client) . await ;
265
+ assert ! ( result. is_err( ) ) ;
266
+ match result {
267
+ Err ( kube_forward:: error:: PortForwardError :: ConnectionError ( msg) ) => {
268
+ assert ! ( msg. contains( "already in use" ) ) ;
269
+ }
270
+ _ => panic ! ( "Expected ConnectionError for port in use" ) ,
271
+ }
272
+ }
273
+
274
+ // This tests specifically depends on kind
275
+ // just a reminder in case it fails later on
276
+ #[ tokio:: test]
277
+ async fn test_monitor_connection ( ) {
278
+ let config = ForwardConfig {
279
+ name : "kube-dns" . to_string ( ) ,
280
+ target : "kube-dns.kube-system" . to_string ( ) ,
281
+ ports : kube_forward:: config:: PortMapping {
282
+ local : 53 ,
283
+ remote : 53 ,
284
+ } ,
285
+ pod_selector : PodSelector {
286
+ label : None ,
287
+ annotation : None ,
288
+ } ,
289
+ local_dns : kube_forward:: config:: LocalDnsConfig {
290
+ enabled : false ,
291
+ hostname : None ,
292
+ } ,
293
+ options : kube_forward:: config:: ForwardOptions {
294
+ max_retries : 3 ,
295
+ retry_interval : Duration :: from_secs ( 1 ) ,
296
+ health_check_interval : Duration :: from_millis ( 100 ) , // Use shorter interval for testing
297
+ } ,
298
+ } ;
299
+
300
+ let service_info = ServiceInfo {
301
+ name : "kube-dns" . to_string ( ) ,
302
+ namespace : "kube-system" . to_string ( ) ,
303
+ ports : vec ! [ 53 ] ,
304
+ } ;
305
+
306
+ let mut forward = PortForward :: new ( config, service_info) ;
307
+ let client = kube:: Client :: try_default ( ) . await . unwrap ( ) ;
308
+
309
+ // Create a listener that we'll close to simulate connection failure
310
+ let listener = TcpListener :: bind ( "127.0.0.1:0" ) . await . unwrap ( ) ;
311
+ let port = listener. local_addr ( ) . unwrap ( ) . port ( ) ;
312
+ forward. config . ports . local = port;
313
+
314
+ // Start monitoring in a separate task
315
+ let monitor_handle = tokio:: spawn ( {
316
+ let forward = forward. clone ( ) ;
317
+ let client = client. clone ( ) ;
318
+ async move { forward. monitor_connection ( & client) . await }
319
+ } ) ;
320
+
321
+ // Wait a bit to ensure monitoring has started
322
+ tokio:: time:: sleep ( Duration :: from_millis ( 50 ) ) . await ;
323
+
324
+ // Close the listener to simulate connection failure
325
+ drop ( listener) ;
326
+
327
+ // Wait for the monitor to detect the failure
328
+ let result = monitor_handle. await . unwrap ( ) ;
329
+ assert ! ( result. is_err( ) ) ;
330
+ match result {
331
+ Err ( kube_forward:: error:: PortForwardError :: ConnectionError ( msg) ) => {
332
+ assert ! ( msg. contains( "Connection health check failed" ) ) ;
333
+ }
334
+ _ => panic ! ( "Expected ConnectionError for health check failure" ) ,
335
+ }
336
+ }
337
+
338
+ #[ tokio:: test]
339
+ async fn test_forward_connection ( ) {
340
+ let pods: kube:: Api < Pod > =
341
+ kube:: Api :: namespaced ( kube:: Client :: try_default ( ) . await . unwrap ( ) , "default" ) ;
342
+
343
+ // Create a mock TCP connection
344
+ let ( client_conn, _server_conn) = tokio:: io:: duplex ( 64 ) ;
345
+
346
+ // Test the forward_connection function
347
+ let result =
348
+ PortForward :: forward_connection ( & pods, "test-pod" . to_string ( ) , 80 , client_conn) . await ;
349
+
350
+ // Since we're not in a real k8s environment, this should fail
351
+ assert ! ( result. is_err( ) ) ;
352
+ }
353
+
354
+ #[ tokio:: test]
355
+ async fn test_get_pod ( ) {
356
+ let config = ForwardConfig {
357
+ name : "test-forward" . to_string ( ) ,
358
+ target : "test-target.test-namespace" . to_string ( ) ,
359
+ ports : kube_forward:: config:: PortMapping {
360
+ local : 8080 ,
361
+ remote : 80 ,
362
+ } ,
363
+ pod_selector : PodSelector {
364
+ label : Some ( "app=test" . to_string ( ) ) ,
365
+ annotation : None ,
366
+ } ,
367
+ local_dns : kube_forward:: config:: LocalDnsConfig {
368
+ enabled : false ,
369
+ hostname : None ,
370
+ } ,
371
+ options : kube_forward:: config:: ForwardOptions {
372
+ max_retries : 3 ,
373
+ retry_interval : Duration :: from_secs ( 1 ) ,
374
+ health_check_interval : Duration :: from_secs ( 5 ) ,
375
+ } ,
376
+ } ;
377
+
378
+ let service_info = ServiceInfo {
379
+ name : "test-service" . to_string ( ) ,
380
+ namespace : "default" . to_string ( ) ,
381
+ ports : vec ! [ 80 ] ,
382
+ } ;
383
+
384
+ let forward = PortForward :: new ( config, service_info) ;
385
+ let client = kube:: Client :: try_default ( ) . await . unwrap ( ) ;
386
+
387
+ // Test get_pod without a real cluster
388
+ let result = forward. get_pod ( & client) . await ;
389
+ assert ! ( result. is_err( ) ) ;
390
+ match result {
391
+ Err ( kube_forward:: error:: PortForwardError :: ConnectionError ( msg) ) => {
392
+ assert ! ( msg. contains( "No ready pods found" ) ) ;
393
+ }
394
+ _ => panic ! ( "Expected ConnectionError for no pods found" ) ,
395
+ }
396
+ }
219
397
}
0 commit comments