Skip to content

Commit d736136

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)
1 parent 4a1113b commit d736136

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;
@@ -56,9 +56,24 @@ use rand::distr::Alphanumeric;
5656
use rand::{rng, Rng};
5757
use serde_json::{json, Value};
5858

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

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

93116
macro_rules! expect_channel_ready_event {
94117
($node:expr, $counterparty_node_id:expr) => {{
95-
match $node.next_event_async().await {
118+
let event = tokio::time::timeout(
119+
std::time::Duration::from_secs(crate::common::INTEROP_TIMEOUT_SECS),
120+
$node.next_event_async(),
121+
)
122+
.await
123+
.unwrap_or_else(|_| {
124+
panic!("{} timed out waiting for ChannelReady event after 60s", $node.node_id())
125+
});
126+
match event {
96127
ref e @ Event::ChannelReady { user_channel_id, counterparty_node_id, .. } => {
97128
println!("{} got event {:?}", $node.node_id(), e);
98129
assert_eq!(counterparty_node_id, Some($counterparty_node_id));
@@ -112,7 +143,15 @@ macro_rules! expect_channel_ready_events {
112143
($node:expr, $counterparty_node_id_a:expr, $counterparty_node_id_b:expr) => {{
113144
let mut ids = Vec::new();
114145
for _ in 0..2 {
115-
match $node.next_event_async().await {
146+
let event = tokio::time::timeout(
147+
std::time::Duration::from_secs(crate::common::INTEROP_TIMEOUT_SECS),
148+
$node.next_event_async(),
149+
)
150+
.await
151+
.unwrap_or_else(|_| {
152+
panic!("{} timed out waiting for ChannelReady event after 60s", $node.node_id())
153+
});
154+
match event {
116155
ref e @ Event::ChannelReady { counterparty_node_id, .. } => {
117156
println!("{} got event {:?}", $node.node_id(), e);
118157
ids.push(counterparty_node_id);
@@ -138,7 +177,15 @@ pub(crate) use expect_channel_ready_events;
138177

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

157204
macro_rules! expect_payment_received_event {
158205
($node:expr, $amount_msat:expr) => {{
159-
match $node.next_event_async().await {
206+
let event = tokio::time::timeout(
207+
std::time::Duration::from_secs(crate::common::INTEROP_TIMEOUT_SECS),
208+
$node.next_event_async(),
209+
)
210+
.await
211+
.unwrap_or_else(|_| {
212+
panic!("{} timed out waiting for PaymentReceived event after 60s", $node.node_id())
213+
});
214+
match event {
160215
ref e @ Event::PaymentReceived { payment_id, amount_msat, .. } => {
161216
println!("{} got event {:?}", $node.node_id(), e);
162217
assert_eq!(amount_msat, $amount_msat);
163218
let payment = $node.payment(&payment_id.unwrap()).unwrap();
164-
if !matches!(payment.kind, PaymentKind::Onchain { .. }) {
219+
if !matches!(payment.kind, ldk_node::payment::PaymentKind::Onchain { .. }) {
165220
assert_eq!(payment.fee_paid_msat, None);
166221
}
167222
$node.event_handled().unwrap();
168223
payment_id
169224
},
170225
ref e => {
171-
panic!("{} got unexpected event!: {:?}", std::stringify!(node_b), e);
226+
panic!("{} got unexpected event!: {:?}", std::stringify!($node), e);
172227
},
173228
}
174229
}};
@@ -178,7 +233,18 @@ pub(crate) use expect_payment_received_event;
178233

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

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

389463
pub(crate) use setup_builder;
390464

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

0 commit comments

Comments
 (0)