Skip to content

Commit 4f81e7d

Browse files
committed
chore: a few more tests
1 parent d045d64 commit 4f81e7d

File tree

2 files changed

+182
-4
lines changed

2 files changed

+182
-4
lines changed

src/forward.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ impl PortForward {
139139
Ok(())
140140
}
141141

142-
async fn monitor_connection(&self, client: &Client) -> Result<()> {
142+
pub async fn monitor_connection(&self, client: &Client) -> Result<()> {
143143
let health_check = HealthCheck::new();
144144
let mut interval = tokio::time::interval(self.config.options.health_check_interval);
145145

@@ -172,7 +172,7 @@ impl PortForward {
172172
}
173173
}
174174

175-
async fn establish_forward(&self, client: &Client) -> Result<()> {
175+
pub async fn establish_forward(&self, client: &Client) -> Result<()> {
176176
self.metrics.record_connection_attempt();
177177
// Get pod for the service
178178
let pod = self.get_pod(client).await?;
@@ -269,7 +269,7 @@ impl PortForward {
269269
Ok(())
270270
}
271271

272-
async fn forward_connection(
272+
pub async fn forward_connection(
273273
pods: &Api<Pod>,
274274
pod_name: String,
275275
port: u16,
@@ -323,7 +323,7 @@ impl PortForward {
323323
Ok(())
324324
}
325325

326-
async fn get_pod(&self, client: &Client) -> Result<Pod> {
326+
pub async fn get_pod(&self, client: &Client) -> Result<Pod> {
327327
let pods: Api<Pod> = Api::namespaced(client.clone(), &self.service_info.namespace);
328328

329329
// Get all pods in the namespace

tests/forward_tests.rs

+178
Original file line numberDiff line numberDiff line change
@@ -216,4 +216,182 @@ mod tests {
216216
let mut rx = forward.shutdown.subscribe();
217217
assert!(rx.try_recv().is_err()); // Should be empty after stop
218218
}
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+
}
219397
}

0 commit comments

Comments
 (0)