diff --git a/crates/cyber-cli/tests/branches_cli.rs b/crates/cyber-cli/tests/branches_cli.rs index 1587e28c..45e07821 100644 --- a/crates/cyber-cli/tests/branches_cli.rs +++ b/crates/cyber-cli/tests/branches_cli.rs @@ -672,11 +672,17 @@ fn reviewed_recovery_busy_confirmation_requires_interrupt_ack_and_a_new_review() .command(&["exec", "--session", &id, "--format", "json", "GATE"]) .stdin(std::process::Stdio::null()) .stdout(std::process::Stdio::null()) - .stderr(std::process::Stdio::null()) + .stderr(std::process::Stdio::piped()) .spawn() .unwrap(); - let deadline = std::time::Instant::now() + Duration::from_secs(10); + // A loaded Windows runner can take well over 10 s to start exec and reach the model. + let deadline = std::time::Instant::now() + Duration::from_secs(60); while model.held().is_empty() { + if let Some(status) = gated.try_wait().unwrap() { + let mut stderr = String::new(); + std::io::Read::read_to_string(gated.stderr.as_mut().unwrap(), &mut stderr).unwrap(); + panic!("exec ended before GATE reached the provider ({status}): {stderr}"); + } assert!( std::time::Instant::now() < deadline, "GATE never reached provider" diff --git a/crates/cyber-server/src/runtime/binary_documents_tests.rs b/crates/cyber-server/src/runtime/binary_documents_tests.rs index 2be545b7..c275f81c 100644 --- a/crates/cyber-server/src/runtime/binary_documents_tests.rs +++ b/crates/cyber-server/src/runtime/binary_documents_tests.rs @@ -245,7 +245,7 @@ async fn disposed_binary_request_retains_worker_ownership_until_actual_exit() { .unwrap() .unwrap(); assert_eq!(paths.available_permits(), 1); - assert!(runtime.inner.activities.lock().unwrap().is_empty()); + activities_drained(&runtime).await; } #[tokio::test] @@ -377,7 +377,7 @@ async fn unwound_binary_worker_does_not_claim_file_success_or_poison_native_proc .location_uncertain .load(std::sync::atomic::Ordering::SeqCst) ); - assert!(runtime.inner.activities.lock().unwrap().is_empty()); + activities_drained(&runtime).await; runtime.shutdown().await; } @@ -543,3 +543,17 @@ async fn recovery_requires_the_configured_exclusive_document_and_leaves_generic_ drop((generic, binary)); runtime.shutdown().await; } + +/// The worker returns its path permit before its activity scope drops and deregisters +/// on the worker's own thread, so the registry empties shortly after; wait for that +/// rather than read it once. +async fn activities_drained(runtime: &Runtime) { + let deadline = std::time::Instant::now() + std::time::Duration::from_secs(5); + while !runtime.inner.activities.lock().unwrap().is_empty() { + assert!( + std::time::Instant::now() < deadline, + "an activity outlived its worker" + ); + tokio::time::sleep(std::time::Duration::from_millis(10)).await; + } +}