From 963235b0bcca717d326ae3e9bf4eccfcedea00c6 Mon Sep 17 00:00:00 2001 From: Piero Noviello <40241799+pieronoviello@users.noreply.github.com> Date: Wed, 4 Nov 2026 08:38:06 +0100 Subject: [PATCH] test: make the JMAP connection-refused check pass on Windows Windows retries a refused loopback connection for about two seconds, longer than the shared 200 ms timeout, so the closed-port request timed out instead of failing to connect. Windows also words the refusal in the system language, so the check for the English word "refused" failed on non-English Windows. Give the closed-port request its own client with a 10 s timeout, and check that the message carries the root cause and that the cause is an io::Error of kind ConnectionRefused. Co-Authored-By: Claude Opus 5.5 --- crates/flectar-mail-core/src/jmap/client.rs | 33 ++++++++++++++++----- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/crates/flectar-mail-core/src/jmap/client.rs b/crates/flectar-mail-core/src/jmap/client.rs index bc06504e..065b4985 100644 --- a/crates/flectar-mail-core/src/jmap/client.rs +++ b/crates/flectar-mail-core/src/jmap/client.rs @@ -1199,18 +1199,37 @@ pub(crate) mod tests { )); held.abort(); - // Nothing listens on a port that was just released. + // Nothing listens on a port that was just released. Windows retries a + // refused connection for about two seconds before reporting it, longer + // than the 200 ms timeout above, so this request gets its own client. let closed = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); let closed_url = format!("http://{}/jmap/", closed.local_addr().unwrap()); drop(closed); - let error = http.post(&closed_url).send().await.unwrap_err(); + let error = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(10)) + .build() + .unwrap() + .post(&closed_url) + .send() + .await + .unwrap_err(); let failure = transport_failure(&error); - assert!( - failure.starts_with(&format!("could not connect ({closed_url}): ")), - "{failure}" + // The OS words the refusal in the system language, so check that the + // message carries the root cause and that the cause is a refusal, + // rather than looking for the English word. + let mut cause: &(dyn std::error::Error + 'static) = &error; + while let Some(source) = cause.source() { + cause = source; + } + assert_eq!( + failure, + format!("could not connect ({closed_url}): {cause}") ); - assert!( - failure.to_ascii_lowercase().contains("refused"), + assert_eq!( + cause + .downcast_ref::() + .map(std::io::Error::kind), + Some(std::io::ErrorKind::ConnectionRefused), "{failure}" ); }