pytest: make pay tests more robust.
What changed, and why it matters
This commit only changes test code in the Core Lightning project's test suite. It makes three payment-related tests more robust by removing unnecessary wait calls, switching from high-level 'pay' to lower-level 'sendpay' in one stress test, and using 'xpay' with shadow routes disabled in another. There are no changes to production code, no bug fixes, and no security-relevant behavior changes.
No security action required. This is a test-maintenance commit. Reviewers may optionally verify that the updated tests still exercise the intended production paths.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff modifies three test files: tests/test_closing.py removes redundant waitsendpay calls after pay; tests/test_connection.py rewrites test_multichan_stress to use sendpay directly instead of pay so the test can force payments over an unreliable channel without the payer learning to avoid it; tests/test_misc.py switches test_listhtlcs_wait to use xpay with dev_use_shadow=False to avoid shadow routes. All changes are test-only and do not alter node or protocol behavior.
Changed components
tests/test_closing.pytests/test_connection.pytests/test_misc.pyInspect captured patch +25 / −7
diff --git a/tests/test_closing.py b/tests/test_closing.py
index 899ee73f..d69e84cd 100644
--- a/tests/test_closing.py
+++ b/tests/test_closing.py
@@ -1247,12 +1247,10 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams, anchors):
# push some money so that 1 + 4 can both send htlcs
inv = l2.rpc.invoice(10**9 // 2, '1', 'balancer')
l1.rpc.pay(inv['bolt11'])
- l1.rpc.waitsendpay(inv['payment_hash'])
wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['htlcs'] == [])
inv = l4.rpc.invoice(10**9 // 2, '1', 'balancer')
l2.rpc.pay(inv['bolt11'])
- l2.rpc.waitsendpay(inv['payment_hash'])
wait_for(lambda: only_one(l1.rpc.listpeerchannels()['channels'])['htlcs'] == [])
# now we send one 'sticky' htlc: l4->l1
diff --git a/tests/test_connection.py b/tests/test_connection.py
index 6a0b38aa..6c8d46c8 100644
--- a/tests/test_connection.py
+++ b/tests/test_connection.py
@@ -3845,6 +3845,7 @@ def test_multichan_stress(node_factory, executor, bitcoind):
l1, l2, l3 = node_factory.line_graph(3, opts={'may_reconnect': True,
'dev-no-reconnect': None})
+ scid23 = first_scid(l2, l3)
# Now fund *second* channel l2->l3 (slightly larger)
bitcoind.rpc.sendtoaddress(l2.rpc.newaddr('bech32')['bech32'], 0.1)
bitcoind.generate_block(1)
@@ -3857,19 +3858,38 @@ def test_multichan_stress(node_factory, executor, bitcoind):
mine_funding_to_announce(bitcoind, [l1, l2, l3], num_blocks=6, wait_for_mempool=1)
wait_for(lambda: len(l1.rpc.listchannels(source=l3.info['id'])['channels']) == 2)
+ # We use sendpay directly here, because xpay learns and refuses to pay!
+ route = [{'amount_msat': 101,
+ 'id': l2.info['id'],
+ 'delay': 16,
+ 'channel': first_scid(l1, l2)},
+ {'amount_msat': 100,
+ 'id': l3.info['id'],
+ 'delay': 10,
+ # We say this, but l2 will choose.
+ 'channel': scid23}]
+
def send_many_payments():
- for i in range(30):
- inv = l3.rpc.invoice(100, "label-" + str(i), "desc")['bolt11']
+ passes = 0
+ fails = 0
+ # Make sure we try many times, and get at least one pass and fail.
+ while passes == 0 or fails == 0 or passes + fails < 30:
+ inv = l3.rpc.invoice(100, "label-" + str(passes + fails), "desc")
+ l1.rpc.sendpay(route, inv['payment_hash'], payment_secret=inv['payment_secret'])
+ time.sleep(0.05)
try:
- l1.rpc.pay(inv)
+ l1.rpc.waitsendpay(inv['payment_hash'])
+ passes += 1
except RpcError:
+ fails += 1
pass
# Send a heap of payments, while reconnecting...
fut = executor.submit(send_many_payments)
- for i in range(10):
+ for _ in range(30):
l3.rpc.disconnect(l2.info['id'], force=True)
+ time.sleep(0.1)
l3.rpc.connect(l2.info['id'], 'localhost', l2.port)
fut.result(TIMEOUT)
diff --git a/tests/test_misc.py b/tests/test_misc.py
index 778ad56f..3e2c6ccf 100644
--- a/tests/test_misc.py
+++ b/tests/test_misc.py
@@ -3674,7 +3674,7 @@ def test_listhtlcs_wait(node_factory, bitcoind, executor):
amt1 = 1000
inv1 = l3.rpc.invoice(amt1, 'inv1', 'desc')
- l1.rpc.pay(inv1['bolt11'])
+ l1.rpc.xpay(invstring=inv1['bolt11'], dev_use_shadow=False)
waitres = waitcreate.result(TIMEOUT)
assert waitres == {'subsystem': 'htlcs',
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.