Skip to content

Commit 9ae7ead

Browse files
committed
Add shared interop test scenarios and entry points
- Shared scenarios in tests/common/scenarios/ generic over ExternalNode - Test entry points for LND, CLN, and Eclair - Combo orchestrator (combo.rs) with interop_combo_tests! macro generating 16 tests per implementation (phase × disconnect side × close type × initiator) - Building blocks use no test_ prefix; full scenarios include setup internally - #[ignore] annotations for known interop failures: CLN keysend (payment_secret issue), Eclair keysend (InvalidOnionPayload), CLN/Eclair splice (version requirements) Co-authored-by: AI (Claude Code)
1 parent e57adf6 commit 9ae7ead

File tree

10 files changed

+1889
-291
lines changed

10 files changed

+1889
-291
lines changed

tests/common/mod.rs

Lines changed: 157 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// http://opensource.org/licenses/MIT>, at your option. You may not use this file except in
66
// accordance with one or both of these licenses.
77

8-
#![cfg(any(test, cln_test, lnd_test, vss_test))]
8+
#![cfg(any(test, cln_test, lnd_test, eclair_test, vss_test))]
99
#![allow(dead_code)]
1010

1111
pub(crate) mod external_node;
@@ -55,9 +55,24 @@ use rand::distr::Alphanumeric;
5555
use rand::{rng, Rng};
5656
use serde_json::{json, Value};
5757

58+
/// Shared timeout (in seconds) for waiting on LDK events and external node operations.
59+
pub(crate) const INTEROP_TIMEOUT_SECS: u64 = 60;
60+
5861
macro_rules! expect_event {
5962
($node:expr, $event_type:ident) => {{
60-
match $node.next_event_async().await {
63+
let event = tokio::time::timeout(
64+
std::time::Duration::from_secs(crate::common::INTEROP_TIMEOUT_SECS),
65+
$node.next_event_async(),
66+
)
67+
.await
68+
.unwrap_or_else(|_| {
69+
panic!(
70+
"{} timed out waiting for {} event after 60s",
71+
$node.node_id(),
72+
std::stringify!($event_type)
73+
)
74+
});
75+
match event {
6176
ref e @ Event::$event_type { .. } => {
6277
println!("{} got event {:?}", $node.node_id(), e);
6378
$node.event_handled().unwrap();
@@ -73,7 +88,15 @@ pub(crate) use expect_event;
7388

7489
macro_rules! expect_channel_pending_event {
7590
($node:expr, $counterparty_node_id:expr) => {{
76-
match $node.next_event_async().await {
91+
let event = tokio::time::timeout(
92+
std::time::Duration::from_secs(crate::common::INTEROP_TIMEOUT_SECS),
93+
$node.next_event_async(),
94+
)
95+
.await
96+
.unwrap_or_else(|_| {
97+
panic!("{} timed out waiting for ChannelPending event after 60s", $node.node_id())
98+
});
99+
match event {
77100
ref e @ Event::ChannelPending { funding_txo, counterparty_node_id, .. } => {
78101
println!("{} got event {:?}", $node.node_id(), e);
79102
assert_eq!(counterparty_node_id, $counterparty_node_id);
@@ -91,7 +114,15 @@ pub(crate) use expect_channel_pending_event;
91114

92115
macro_rules! expect_channel_ready_event {
93116
($node:expr, $counterparty_node_id:expr) => {{
94-
match $node.next_event_async().await {
117+
let event = tokio::time::timeout(
118+
std::time::Duration::from_secs(crate::common::INTEROP_TIMEOUT_SECS),
119+
$node.next_event_async(),
120+
)
121+
.await
122+
.unwrap_or_else(|_| {
123+
panic!("{} timed out waiting for ChannelReady event after 60s", $node.node_id())
124+
});
125+
match event {
95126
ref e @ Event::ChannelReady { user_channel_id, counterparty_node_id, .. } => {
96127
println!("{} got event {:?}", $node.node_id(), e);
97128
assert_eq!(counterparty_node_id, Some($counterparty_node_id));
@@ -111,7 +142,15 @@ macro_rules! expect_channel_ready_events {
111142
($node:expr, $counterparty_node_id_a:expr, $counterparty_node_id_b:expr) => {{
112143
let mut ids = Vec::new();
113144
for _ in 0..2 {
114-
match $node.next_event_async().await {
145+
let event = tokio::time::timeout(
146+
std::time::Duration::from_secs(crate::common::INTEROP_TIMEOUT_SECS),
147+
$node.next_event_async(),
148+
)
149+
.await
150+
.unwrap_or_else(|_| {
151+
panic!("{} timed out waiting for ChannelReady event after 60s", $node.node_id())
152+
});
153+
match event {
115154
ref e @ Event::ChannelReady { counterparty_node_id, .. } => {
116155
println!("{} got event {:?}", $node.node_id(), e);
117156
ids.push(counterparty_node_id);
@@ -137,7 +176,15 @@ pub(crate) use expect_channel_ready_events;
137176

138177
macro_rules! expect_splice_pending_event {
139178
($node:expr, $counterparty_node_id:expr) => {{
140-
match $node.next_event_async().await {
179+
let event = tokio::time::timeout(
180+
std::time::Duration::from_secs(crate::common::INTEROP_TIMEOUT_SECS),
181+
$node.next_event_async(),
182+
)
183+
.await
184+
.unwrap_or_else(|_| {
185+
panic!("{} timed out waiting for SplicePending event after 60s", $node.node_id())
186+
});
187+
match event {
141188
ref e @ Event::SplicePending { new_funding_txo, counterparty_node_id, .. } => {
142189
println!("{} got event {:?}", $node.node_id(), e);
143190
assert_eq!(counterparty_node_id, $counterparty_node_id);
@@ -155,19 +202,27 @@ pub(crate) use expect_splice_pending_event;
155202

156203
macro_rules! expect_payment_received_event {
157204
($node:expr, $amount_msat:expr) => {{
158-
match $node.next_event_async().await {
205+
let event = tokio::time::timeout(
206+
std::time::Duration::from_secs(crate::common::INTEROP_TIMEOUT_SECS),
207+
$node.next_event_async(),
208+
)
209+
.await
210+
.unwrap_or_else(|_| {
211+
panic!("{} timed out waiting for PaymentReceived event after 60s", $node.node_id())
212+
});
213+
match event {
159214
ref e @ Event::PaymentReceived { payment_id, amount_msat, .. } => {
160215
println!("{} got event {:?}", $node.node_id(), e);
161216
assert_eq!(amount_msat, $amount_msat);
162217
let payment = $node.payment(&payment_id.unwrap()).unwrap();
163-
if !matches!(payment.kind, PaymentKind::Onchain { .. }) {
218+
if !matches!(payment.kind, ldk_node::payment::PaymentKind::Onchain { .. }) {
164219
assert_eq!(payment.fee_paid_msat, None);
165220
}
166221
$node.event_handled().unwrap();
167222
payment_id
168223
},
169224
ref e => {
170-
panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e);
225+
panic!("{} got unexpected event!: {:?}", std::stringify!($node), e);
171226
},
172227
}
173228
}};
@@ -177,7 +232,18 @@ pub(crate) use expect_payment_received_event;
177232

178233
macro_rules! expect_payment_claimable_event {
179234
($node:expr, $payment_id:expr, $payment_hash:expr, $claimable_amount_msat:expr) => {{
180-
match $node.next_event_async().await {
235+
let event = tokio::time::timeout(
236+
std::time::Duration::from_secs(crate::common::INTEROP_TIMEOUT_SECS),
237+
$node.next_event_async(),
238+
)
239+
.await
240+
.unwrap_or_else(|_| {
241+
panic!(
242+
"{} timed out waiting for PaymentClaimable event after 60s",
243+
std::stringify!($node)
244+
)
245+
});
246+
match event {
181247
ref e @ Event::PaymentClaimable {
182248
payment_id,
183249
payment_hash,
@@ -202,7 +268,15 @@ pub(crate) use expect_payment_claimable_event;
202268

203269
macro_rules! expect_payment_successful_event {
204270
($node:expr, $payment_id:expr, $fee_paid_msat:expr) => {{
205-
match $node.next_event_async().await {
271+
let event = tokio::time::timeout(
272+
std::time::Duration::from_secs(crate::common::INTEROP_TIMEOUT_SECS),
273+
$node.next_event_async(),
274+
)
275+
.await
276+
.unwrap_or_else(|_| {
277+
panic!("{} timed out waiting for PaymentSuccessful event after 60s", $node.node_id())
278+
});
279+
match event {
206280
ref e @ Event::PaymentSuccessful { payment_id, fee_paid_msat, .. } => {
207281
println!("{} got event {:?}", $node.node_id(), e);
208282
if let Some(fee_msat) = $fee_paid_msat {
@@ -390,6 +464,9 @@ macro_rules! setup_builder {
390464

391465
pub(crate) use setup_builder;
392466

467+
#[cfg(any(cln_test, lnd_test, eclair_test))]
468+
pub(crate) mod scenarios;
469+
393470
pub(crate) fn setup_two_nodes(
394471
chain_source: &TestChainSource, allow_0conf: bool, anchor_channels: bool,
395472
anchors_trusted_no_reserve: bool,
@@ -1699,3 +1776,72 @@ impl TestSyncStoreInner {
16991776
self.do_list(primary_namespace, secondary_namespace)
17001777
}
17011778
}
1779+
1780+
/// Generates 16 individual `#[tokio::test]` functions covering every combination
1781+
/// of (Phase, disconnect Side, CloseType, close Side):
1782+
/// 2 phases × 2 disconnect sides × 2 close types × 2 close initiators = 16.
1783+
///
1784+
/// PayType is fixed to Bolt11 — keysend vs bolt11 doesn't affect channel close
1785+
/// behavior and is already covered by dedicated named tests (`test_*_receive_keysend`,
1786+
/// `test_*_receive_payments`). Do NOT add a Keysend axis here: CLN and Eclair
1787+
/// have known keysend interop issues (see `#[ignore]` tests in their entry points).
1788+
///
1789+
/// Usage (inside each `integration_tests_*.rs`):
1790+
/// ```ignore
1791+
/// interop_combo_tests!(test_lnd, setup_clients, setup_ldk_node);
1792+
/// ```
1793+
#[macro_export]
1794+
macro_rules! interop_combo_tests {
1795+
($prefix:ident, $setup_clients:ident, $setup_ldk_node:ident) => {
1796+
$crate::interop_combo_tests!(
1797+
@phase $prefix, $setup_clients, $setup_ldk_node,
1798+
[payment, Phase::Payment], [idle, Phase::Idle]
1799+
);
1800+
};
1801+
1802+
(@phase $prefix:ident, $sc:ident, $sn:ident, $([$pn:ident, $pv:expr]),+) => {
1803+
$(
1804+
$crate::interop_combo_tests!(
1805+
@disc $prefix, $sc, $sn, $pn, $pv,
1806+
[ldk, Side::Ldk], [ext, Side::External]
1807+
);
1808+
)+
1809+
};
1810+
1811+
(@disc $prefix:ident, $sc:ident, $sn:ident, $pn:ident, $pv:expr, $([$dn:ident, $dv:expr]),+) => {
1812+
$(
1813+
$crate::interop_combo_tests!(
1814+
@close $prefix, $sc, $sn, $pn, $pv, $dn, $dv,
1815+
[coop, CloseType::Cooperative], [force, CloseType::Force]
1816+
);
1817+
)+
1818+
};
1819+
1820+
(@close $prefix:ident, $sc:ident, $sn:ident, $pn:ident, $pv:expr, $dn:ident, $dv:expr,
1821+
$([$cn:ident, $cv:expr]),+) => {
1822+
$(
1823+
$crate::interop_combo_tests!(
1824+
@ci $prefix, $sc, $sn, $pn, $pv, $dn, $dv, $cn, $cv,
1825+
[ldk, Side::Ldk], [ext, Side::External]
1826+
);
1827+
)+
1828+
};
1829+
1830+
(@ci $prefix:ident, $sc:ident, $sn:ident, $pn:ident, $pv:expr, $dn:ident, $dv:expr,
1831+
$cn:ident, $cv:expr, $([$cin:ident, $civ:expr]),+) => {
1832+
$(
1833+
paste::paste! {
1834+
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
1835+
async fn [<$prefix _combo_ $pn _ $dn _ $cn _ $cin>]() {
1836+
let (bitcoind, electrs, peer) = $sc().await;
1837+
let node = $sn();
1838+
run_interop_combo_test(
1839+
&node, &peer, &bitcoind, &electrs,
1840+
$pv, $dv, $cv, $civ, PayType::Bolt11,
1841+
).await;
1842+
node.stop().unwrap();
1843+
}
1844+
}
1845+
)+
1846+
};
1847+
}

0 commit comments

Comments
 (0)