From 1a4a71ef98d1661726ceed71ab8cf74cd5aad3c0 Mon Sep 17 00:00:00 2001 From: Simon Peter Rothgang Date: Mon, 7 Sep 2026 18:55:25 +0200 Subject: [PATCH] test(mention): fail loudly when background search never settles - Replace the fixed 200-iteration poll in `run_search` with a deadline so the wait scales past the previous 1s budget - Assert on timeout instead of returning silently, reporting index status, search status and pending match sequence - Drain events before sleeping so an already-settled search returns without an extra poll interval - Extract `SEARCH_SETTLE_TIMEOUT` and `SEARCH_SETTLE_POLL_INTERVAL` to document that the budget targets the slowest CI runner --- src/app/mention.rs | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/app/mention.rs b/src/app/mention.rs index c6b25b1..999c807 100644 --- a/src/app/mention.rs +++ b/src/app/mention.rs @@ -947,6 +947,11 @@ mod tests { use std::path::PathBuf; use std::time::Duration; + /// Budget for the background walk and matcher to settle before a test reads + /// mention state. Sized for the slowest CI runner, not local hardware. + const SEARCH_SETTLE_TIMEOUT: Duration = Duration::from_secs(30); + const SEARCH_SETTLE_POLL_INTERVAL: Duration = Duration::from_millis(5); + fn app_with_temp_files(files: &[&str]) -> (App, tempfile::TempDir) { let tmp = tempfile::tempdir().expect("tempdir"); for file in files { @@ -992,20 +997,32 @@ mod tests { file_index::restart(app); } + /// Drives the background file index until the mention search settles, and + /// fails loudly if it does not. Returning silently on timeout would let a + /// test assert against a half-scanned index and report a candidate + /// mismatch instead of the timeout that actually caused it. fn run_search(app: &mut App) { - for _ in 0..200 { + let deadline = Instant::now() + SEARCH_SETTLE_TIMEOUT; + loop { crate::app::file_index::drain_events(app); - std::thread::sleep(Duration::from_millis(5)); let index_is_settled = !matches!(app.file_index.status, file_index::FileIndexStatus::Scanning); let mention_is_settled = app.mention.as_ref().is_none_or(|mention| { mention.pending_match_sequence.is_none() && !matches!(mention.search_status, MentionSearchStatus::Searching) }); - let is_settled = index_is_settled && mention_is_settled; - if is_settled { + if index_is_settled && mention_is_settled { return; } + assert!( + Instant::now() < deadline, + "mention search did not settle within {SEARCH_SETTLE_TIMEOUT:?}: \ + index_status={:?}, search_status={:?}, pending_match_sequence={:?}", + app.file_index.status, + app.mention.as_ref().map(|mention| mention.search_status), + app.mention.as_ref().and_then(|mention| mention.pending_match_sequence), + ); + std::thread::sleep(SEARCH_SETTLE_POLL_INTERVAL); } }