From bfd27848196e76859114b5dc989bfdfe61dc5468 Mon Sep 17 00:00:00 2001 From: John Coffey Date: Sat, 19 Sep 2026 21:17:21 -0700 Subject: [PATCH] Compat: the copy's pending tasks aren't this run's to wait for MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The run against INBUXA's store hung printing "Waiting for pending task AcmeRenewal(...)": the copy carries that server's task queue, and a renewal due in 2026-11 will not come due while a test watches it. Under NO_INSERT the wait now skips tasks that aren't due and ones that have permanently failed, which leaves the tasks the test itself caused — a restore in undelete_compat comes due at once — and gives up after a minute with the offending task printed. A test that was really waiting on its own work now fails on its assertion, which says more than a spinner. Ordinary runs are untouched: system_tests, which waits on tasks throughout, still passes in 135s. --- docs/spec/compat-tests.md | 9 +++++++++ tests/src/utils/storage.rs | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/docs/spec/compat-tests.md b/docs/spec/compat-tests.md index 4276fa9..32f4507 100644 --- a/docs/spec/compat-tests.md +++ b/docs/spec/compat-tests.md @@ -109,6 +109,15 @@ fails on one of its own (`tests/src/utils/server.rs`). The copy is not edited to achieve this; its listeners are simply not what a compat run needs. +**Why the copy's task queue is ignored.** A real server's pending tasks come +with its store: ACME renewals due months from now, work that can never run +on this machine. The harness waits for tasks to drain, so the first run +against INBUXA's copy sat printing `Waiting for pending task +AcmeRenewal(...)` until it was interrupted. Under `NO_INSERT` it now skips +tasks that aren't due and ones that have failed for good, and gives up +after a minute with the task printed, so the test fails on its own +assertion instead of spinning. + **Why `compat-` listeners appear in the copy.** The harness needs listeners on its own ports, and the registry keys listeners by name. A real server has its own, and a production listener called `jmap` or `imap` collided diff --git a/tests/src/utils/storage.rs b/tests/src/utils/storage.rs index 414d04c..36b27c6 100644 --- a/tests/src/utils/storage.rs +++ b/tests/src/utils/storage.rs @@ -281,6 +281,19 @@ async fn build_search_store(typ: SearchStoreType, _path: &str) -> SearchStore { } pub async fn wait_for_tasks(server: &Server, skip_not_due: bool, skip_permanent_failures: bool) { + // inbuxa: a compat run opens a copy of a real server's store, and its + // task queue comes too: ACME renewals due months from now, work that + // can never run on this machine. Waiting for that to drain never ends, + // so skip what isn't due and what has already failed for good, and give + // up after a minute rather than hang. Whatever the test was waiting for + // then fails on its own assertion, which says more than a spinner. + let compat = std::env::var("NO_INSERT").is_ok(); + let (skip_not_due, skip_permanent_failures) = if compat { + (true, true) + } else { + (skip_not_due, skip_permanent_failures) + }; + let give_up_at = std::time::Instant::now() + std::time::Duration::from_secs(60); let mut count = 0; loop { let mut has_index_tasks = None; @@ -315,6 +328,12 @@ pub async fn wait_for_tasks(server: &Server, skip_not_due: bool, skip_permanent_ if count % 10 == 0 { println!("Waiting for pending task {:?}...", task); } + if compat && std::time::Instant::now() > give_up_at { + println!( + "Gave up waiting after 60s; this task is the copy's, not this run's: {task:?}" + ); + break; + } tokio::time::sleep(std::time::Duration::from_millis(200)).await; } else { break;