Skip to content

Commit 6724818

Browse files
committed
fix(test): isolate mock router/node/dns state per test thread
MockRouter/MockNode/MockDnsServer shared process-global LazyLock state so that a test's own handle and the one the code-under-test builds via get_router() see the same state. But being process-global, parallel tests contaminated each other (13 failures under the default parallel test runner; green only with --test-threads=1). Switch the shared maps to thread_local!. #[tokio::test] uses a current-thread runtime and libtest runs each test on its own thread, so all of a test's async work (and every Mock*::new()) runs on one thread: this keeps the intra-test sharing while isolating parallel tests. Mock routers are only ever constructed under #[cfg(test)], so per-thread scoping is safe.
1 parent ad657a4 commit 6724818

1 file changed

Lines changed: 38 additions & 23 deletions

File tree

lnvps_api/src/mocks.rs

Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ use ssh2::HashType::Sha256;
3939
use std::collections::HashMap;
4040
use std::ops::Add;
4141
use std::pin::Pin;
42-
use std::sync::{Arc, LazyLock};
42+
use std::sync::Arc;
4343
use std::time::Duration;
4444
use tokio::sync::Mutex;
4545

@@ -59,24 +59,31 @@ impl Default for MockRouter {
5959

6060
impl MockRouter {
6161
pub fn new() -> Self {
62-
static LAZY_ARP: LazyLock<Arc<Mutex<HashMap<u64, ArpEntry>>>> =
63-
LazyLock::new(|| Arc::new(Mutex::new(HashMap::new())));
64-
static LAZY_TUNNELS: LazyLock<Arc<Mutex<HashMap<String, Tunnel>>>> =
65-
LazyLock::new(|| Arc::new(Mutex::new(HashMap::new())));
66-
static LAZY_SESSIONS: LazyLock<Arc<Mutex<HashMap<String, BgpSession>>>> =
67-
LazyLock::new(|| Arc::new(Mutex::new(HashMap::new())));
68-
static LAZY_DEFAULT_ROUTE: LazyLock<Arc<Mutex<Option<BgpRoute>>>> = LazyLock::new(|| {
69-
Arc::new(Mutex::new(Some(BgpRoute {
70-
prefix: "0.0.0.0/0".to_string(),
71-
next_hop: Some("192.0.2.1".to_string()),
72-
})))
73-
});
62+
// Per-test-thread state (NOT a process-global): every `MockRouter`
63+
// built on the same thread — the test's own handle and the one the
64+
// code-under-test gets from `get_router()` — shares one state, while
65+
// tests running in parallel on different libtest threads stay isolated.
66+
// `#[tokio::test]` uses a current-thread runtime, so all of a test's
67+
// async work (and thus every `MockRouter::new()`) runs on its thread.
68+
thread_local! {
69+
static TL_ARP: Arc<Mutex<HashMap<u64, ArpEntry>>> =
70+
Arc::new(Mutex::new(HashMap::new()));
71+
static TL_TUNNELS: Arc<Mutex<HashMap<String, Tunnel>>> =
72+
Arc::new(Mutex::new(HashMap::new()));
73+
static TL_SESSIONS: Arc<Mutex<HashMap<String, BgpSession>>> =
74+
Arc::new(Mutex::new(HashMap::new()));
75+
static TL_DEFAULT_ROUTE: Arc<Mutex<Option<BgpRoute>>> =
76+
Arc::new(Mutex::new(Some(BgpRoute {
77+
prefix: "0.0.0.0/0".to_string(),
78+
next_hop: Some("192.0.2.1".to_string()),
79+
})));
80+
}
7481

7582
Self {
76-
arp: LAZY_ARP.clone(),
77-
tunnels: LAZY_TUNNELS.clone(),
78-
sessions: LAZY_SESSIONS.clone(),
79-
default_route: LAZY_DEFAULT_ROUTE.clone(),
83+
arp: TL_ARP.with(|a| a.clone()),
84+
tunnels: TL_TUNNELS.with(|t| t.clone()),
85+
sessions: TL_SESSIONS.with(|s| s.clone()),
86+
default_route: TL_DEFAULT_ROUTE.with(|d| d.clone()),
8087
}
8188
}
8289

@@ -343,10 +350,14 @@ pub struct MockInvoice {
343350

344351
impl MockNode {
345352
pub fn new() -> Self {
346-
static LAZY_INVOICES: LazyLock<Arc<Mutex<HashMap<String, MockInvoice>>>> =
347-
LazyLock::new(|| Arc::new(Mutex::new(HashMap::new())));
353+
// Per-test-thread state (see `MockRouter::new`): isolates parallel
354+
// tests while sharing within a single test.
355+
thread_local! {
356+
static TL_INVOICES: Arc<Mutex<HashMap<String, MockInvoice>>> =
357+
Arc::new(Mutex::new(HashMap::new()));
358+
}
348359
Self {
349-
invoices: LAZY_INVOICES.clone(),
360+
invoices: TL_INVOICES.with(|i| i.clone()),
350361
}
351362
}
352363
}
@@ -418,10 +429,14 @@ impl Default for MockDnsServer {
418429

419430
impl MockDnsServer {
420431
pub fn new() -> Self {
421-
static LAZY_ZONES: LazyLock<Arc<Mutex<HashMap<String, HashMap<String, MockDnsEntry>>>>> =
422-
LazyLock::new(|| Arc::new(Mutex::new(HashMap::new())));
432+
// Per-test-thread state (see `MockRouter::new`): isolates parallel
433+
// tests while sharing within a single test.
434+
thread_local! {
435+
static TL_ZONES: Arc<Mutex<HashMap<String, HashMap<String, MockDnsEntry>>>> =
436+
Arc::new(Mutex::new(HashMap::new()));
437+
}
423438
Self {
424-
zones: LAZY_ZONES.clone(),
439+
zones: TL_ZONES.with(|z| z.clone()),
425440
}
426441
}
427442

0 commit comments

Comments
 (0)