diff --git a/desktop/src-tauri/src/relay.rs b/desktop/src-tauri/src/relay.rs index 71aa21c413..7b636a4a82 100644 --- a/desktop/src-tauri/src/relay.rs +++ b/desktop/src-tauri/src/relay.rs @@ -652,9 +652,12 @@ mod tests { #[tokio::test] async fn oversized_hint_is_capped_in_relay_error_message_string() { - use crate::relay_admission::MAX_HINT_SECONDS; + use crate::relay_admission::{reset_rate_limit_gate, MAX_HINT_SECONDS, TEST_SERIAL}; use std::io::{Read as _, Write as _}; + let _serial = TEST_SERIAL.lock().await; + reset_rate_limit_gate(); + // Use a std::net listener on a std::thread — the same pattern as the // relay_admission loopback tests. This avoids two races that cause CI // failures with tokio::net + into_std(): @@ -702,6 +705,7 @@ mod tests { !msg.contains(&oversized.to_string()), "raw oversized hint must not appear in the message string" ); + reset_rate_limit_gate(); } // ── effective_agent_relay_url: legacy pin ignored ───────────────────────── diff --git a/desktop/src-tauri/src/relay_admission.rs b/desktop/src-tauri/src/relay_admission.rs index 15222f8590..4b0dd1f369 100644 --- a/desktop/src-tauri/src/relay_admission.rs +++ b/desktop/src-tauri/src/relay_admission.rs @@ -42,6 +42,10 @@ pub const MAX_HINT_SECONDS: u64 = 300; static GATE_EXPIRY: Mutex> = Mutex::new(None); +// The gate is process-wide, so every test that can arm it must serialize. +#[cfg(test)] +pub(crate) static TEST_SERIAL: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(()); + /// Arm (or extend) the admission gate from a relay 429. /// /// `retry_in_seconds` is the parsed `retry in Ns` hint, if the relay provided @@ -105,9 +109,8 @@ mod tests { use super::*; // The gate is a process-wide static shared by every test in this binary, - // so all gate tests serialize on one async lock to keep armed expiries + // so all tests that arm it serialize on one async lock to keep expiries // from bleeding between parallel test threads. - pub(crate) static TEST_SERIAL: tokio::sync::Mutex<()> = tokio::sync::Mutex::const_new(()); #[tokio::test(start_paused = true)] async fn wait_returns_immediately_when_gate_is_inactive() {