flake: fix in-flight statuses causing flakyness by waiting for all xpay-n layers to be removed from askrene before checking settlement outcome
What changed, and why it matters
This commit fixes a flaky automated test, not a security bug. It adds a wait so the test only checks payment statuses after internal cleanup layers called 'xpay-<n>' have been removed. There is no change to production code or user-facing behavior.
No security action needed. Treat as a normal test reliability improvement.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change is confined to tests/test_pay.py in the test_sendpay_grouping test. It inserts a wait_for() call that polls askrene_listlayers() until no layers prefixed with ‘xpay-’ remain, then refreshes listpays() before asserting the expected statuses [‘failed’, ‘failed’, ‘complete’]. The comment explains that xpay returns failure to the RPC caller before finishing cleanup, so statuses can briefly appear ‘pending’ due to attempt_ongoing() still being true. This is purely a test synchronization fix.
Changed components
tests/test_pay.py::test_sendpay_groupingInspect captured patch +11 / −0
diff --git a/tests/test_pay.py b/tests/test_pay.py
index 0f357547..04786bc5 100644
--- a/tests/test_pay.py
+++ b/tests/test_pay.py
@@ -5312,6 +5312,17 @@ def test_sendpay_grouping(node_factory, bitcoind):
# And finally we should have all 3 attempts to pay the invoice
pays = l1.rpc.listpays()['pays']
assert(len(pays) == 3)
+
+ # xpay returns the failure to the caller before it finishes cleaning up
+ # the payment, so a failed attempt can still be reported as 'pending'
+ # (attempt_ongoing() -> true) right after the rpc returns. Each xpay payment
+ # owns a private "xpay-<n>" askrene layer which is removed in that same cleanup,
+ # so wait for them all to disappear before checking the exact statuses.
+ # If payment hangs indefinetly, the default timeout will fail the test
+ # (60s/180s if SLOW_MACHINE=1).
+ wait_for(lambda: not any(layer['layer'].startswith('xpay-')
+ for layer in l1.rpc.askrene_listlayers()['layers']))
+ pays = l1.rpc.listpays()['pays']
assert([p['status'] for p in pays] == ['failed', 'failed', 'complete'])
Why this scored 14/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.