From 557c05645fb23aa24c4c652a5bfd5608bc2100fe Mon Sep 17 00:00:00 2001 From: Duncan Date: Wed, 19 Aug 2026 16:51:11 -0400 Subject: [PATCH] test(desktop): serialize archive tests against the egress registry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The merge unites main's identity-archive tests, which transitively admit an owner-identity egress lease through submit_event/signing_keys, with this branch's owner_identity_egress tests, which latch the process-global registry into Draining/Indeterminate. Neither parent had both test sets. The archive tests serialized only on the rate-limit TEST_SERIAL, not the EGRESS_REGISTRY_TEST_LOCK the egress tests use, so a concurrent egress test's drain window leaked in and refused the signer — a reproducible "owner-identity egress is draining" / empty-snapshot failure under the full parallel lib run, passing in isolation. Hold EGRESS_REGISTRY_TEST_LOCK and reset the registry in the three egress-touching archive tests, matching the module's own guard idiom and the TEST_SERIAL-first lock order of the egress admission tests. Co-authored-by: Will Pfleger Signed-off-by: Will Pfleger --- .../src/commands/identity_archive.rs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/desktop/src-tauri/src/commands/identity_archive.rs b/desktop/src-tauri/src/commands/identity_archive.rs index 3020399e67..04d5eb543d 100644 --- a/desktop/src-tauri/src/commands/identity_archive.rs +++ b/desktop/src-tauri/src/commands/identity_archive.rs @@ -649,12 +649,22 @@ mod tests { /// RED-on-revert: restore `fetch_archived_pubkeys` to read the override for /// each leg (`fetch_relay_self` + `query_relay`) and this returns B's pubkey. #[tokio::test] + #[allow(clippy::await_holding_lock)] // EGRESS_REGISTRY_TEST_LOCK serialises parallel tests async fn archived_fetch_never_crosses_relays_mid_flight() { use crate::app_state::build_app_state; use crate::relay_admission::{reset_rate_limit_gate, TEST_SERIAL}; use axum::{routing::get, routing::post, Json, Router}; let _serial = TEST_SERIAL.lock().await; + // The archive fetch signs through `signing_keys`, gated by the + // process-global egress registry, so serialise against it too — not + // just the rate-limit `TEST_SERIAL`. Without this a concurrent egress + // test's `Draining`/`Indeterminate` window leaks in and refuses the + // signer. Order matches the egress admission tests: TEST_SERIAL first. + let _egress_guard = crate::owner_identity_egress::EGRESS_REGISTRY_TEST_LOCK + .lock() + .unwrap_or_else(|e| e.into_inner()); + crate::owner_identity_egress::reset_registry_for_test(); reset_rate_limit_gate(); // Build a loopback relay that advertises `relay_keys` as its NIP-11 @@ -764,11 +774,21 @@ mod tests { /// this pins the archive command's callback independently of unarchive. #[cfg(not(target_os = "windows"))] #[tokio::test] + #[allow(clippy::await_holding_lock)] // EGRESS_REGISTRY_TEST_LOCK serialises parallel tests async fn archive_core_fires_regen_only_on_accepted_submit() { use crate::app_state::build_app_state; use crate::relay_admission::{reset_rate_limit_gate, TEST_SERIAL}; let _serial = TEST_SERIAL.lock().await; + // The submit path admits an owner-identity egress lease, gated by the + // process-global egress registry, so serialise against it too — not + // just the rate-limit `TEST_SERIAL`. Without this a concurrent egress + // test's `Draining`/`Indeterminate` window leaks in and refuses the + // submit. Order matches the egress admission tests: TEST_SERIAL first. + let _egress_guard = crate::owner_identity_egress::EGRESS_REGISTRY_TEST_LOCK + .lock() + .unwrap_or_else(|e| e.into_inner()); + crate::owner_identity_egress::reset_registry_for_test(); reset_rate_limit_gate(); let state = build_app_state(); @@ -814,11 +834,21 @@ mod tests { /// independently, not just the shared `submit_then_regenerate`. #[cfg(not(target_os = "windows"))] #[tokio::test] + #[allow(clippy::await_holding_lock)] // EGRESS_REGISTRY_TEST_LOCK serialises parallel tests async fn unarchive_core_fires_regen_only_on_accepted_submit() { use crate::app_state::build_app_state; use crate::relay_admission::{reset_rate_limit_gate, TEST_SERIAL}; let _serial = TEST_SERIAL.lock().await; + // The submit path admits an owner-identity egress lease, gated by the + // process-global egress registry, so serialise against it too — not + // just the rate-limit `TEST_SERIAL`. Without this a concurrent egress + // test's `Draining`/`Indeterminate` window leaks in and refuses the + // submit. Order matches the egress admission tests: TEST_SERIAL first. + let _egress_guard = crate::owner_identity_egress::EGRESS_REGISTRY_TEST_LOCK + .lock() + .unwrap_or_else(|e| e.into_inner()); + crate::owner_identity_egress::reset_registry_for_test(); reset_rate_limit_gate(); let state = build_app_state();