From 2d0512cc68e0420ec6114d23d06e6e2c1b0f5eb0 Mon Sep 17 00:00:00 2001 From: d0ubl3g <48954024+d0ubl3g@users.noreply.github.com> Date: Thu, 15 Oct 2026 18:38:54 +0200 Subject: [PATCH] Keep the refused-connection test from racing other tests a_connection_error_is_retried bound a listener on an ephemeral port, dropped it and expected every connection to that port to be refused. Between the drop and the client's attempts, another test running in parallel (mockito servers bind ephemeral ports) could receive the same port, so the client got an answer and the retry count was not 2. The test now keeps the listener bound for its whole duration, so the port cannot be reused. The listener never accepts, so each attempt times out (short client timeout), which the client retries the same way as a refused connection. A reset after accept would not be retried, as it is not a connect error, so accepting and closing was not used. The assertions are unchanged: Error::Transport and exactly 2 retries. Closes #183 Signed-off-by: d0ubl3g <48954024+d0ubl3g@users.noreply.github.com> --- crates/hodeishield-api/tests/api.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/hodeishield-api/tests/api.rs b/crates/hodeishield-api/tests/api.rs index 5e5d391..4be6f57 100644 --- a/crates/hodeishield-api/tests/api.rs +++ b/crates/hodeishield-api/tests/api.rs @@ -585,17 +585,18 @@ fn a_503_with_retry_after_longer_than_max_wait_does_not_retry() { #[test] fn a_connection_error_is_retried() { - // Bind to port 0 to get an ephemeral port, then drop the listener to free it. - // The next connection attempt to that port will be refused. + // Keep the listener bound for the whole test so no other test can take the port. It never + // accepts, so every attempt connects (the kernel completes the handshake) and then gets no + // answer: a timeout, which is retried like a refused connection. let listener = std::net::TcpListener::bind(("127.0.0.1", 0)).expect("bind"); let port = listener.local_addr().expect("addr").port(); - drop(listener); let url = format!("http://127.0.0.1:{port}"); let retry_count = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0)); let counted = std::sync::Arc::clone(&retry_count); let result = Client::builder(url.parse().expect("url"), SecretString::from("hsk_example")) + .timeout(Duration::from_millis(200)) .transient_retries(2, Duration::from_millis(1)) .retry_observer(move |_event| { counted.fetch_add(1, std::sync::atomic::Ordering::SeqCst); @@ -608,6 +609,7 @@ fn a_connection_error_is_retried() { assert!(matches!(result.expect_err("error"), Error::Transport(_))); let retries = retry_count.load(std::sync::atomic::Ordering::SeqCst); assert_eq!(retries, 2, "should have retried exactly 2 times"); + drop(listener); } #[test]