From ca8bdba0c5627208075dd023239a4d27244010be Mon Sep 17 00:00:00 2001 From: SomberNight Date: Sun, 19 Apr 2026 15:15:39 +0000 Subject: [PATCH] tests: lnpeer: fix flaky test "hold_invoice_set_doesnt_get_expired" This test was flaky: the mpp_set resolution gets set to SETTLING several asyncio event loop iterations before the hold invoice callback "cb" gets called. If the 0.1 sec polling triggers just in the middle of that interval, `assert cb_got_called` fails. ``` async def check_mpp_state(): async def wait_for_resolution(): while True: await asyncio.sleep(0.1) if payment_key not in bob_w.received_mpp_htlcs: continue if not bob_w.received_mpp_htlcs[payment_key].resolution == RecvMPPResolution.SETTLING: continue return await util.wait_for2(wait_for_resolution(), timeout=2) > assert cb_got_called E assert False tests/test_lnpeer.py:1898: AssertionError ``` see https://github.com/spesmilo/electrum/blob/16c8cb50e38c274cce8f9f66f28d8dd453f9f074/electrum/lnpeer.py#L3136-L3137 fixes https://github.com/spesmilo/electrum/issues/10589 ----- diff to reproduce the failure without present patch: ``` diff --git a/tests/test_lnpeer.py b/tests/test_lnpeer.py index 8669931c24..e15973d68f 100644 --- a/tests/test_lnpeer.py +++ b/tests/test_lnpeer.py @@ -1885,6 +1885,7 @@ class TestPeerDirect(TestPeer): cb_got_called = False async def cb(_payment_hash): self.logger.debug(f"hold invoice callback called. {bob_w.network.get_local_height()=}") + await asyncio.sleep(1) nonlocal cb_got_called cb_got_called = True ``` --- tests/test_lnpeer.py | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/tests/test_lnpeer.py b/tests/test_lnpeer.py index 8669931c2..829af60ea 100644 --- a/tests/test_lnpeer.py +++ b/tests/test_lnpeer.py @@ -1882,25 +1882,15 @@ class TestPeerDirect(TestPeer): del bob_w._preimages[pay_req.rhash] # del preimage so bob doesn't settle payment_key = bob_w._get_payment_key(lnaddr.paymenthash).hex() - cb_got_called = False + cb_got_called = asyncio.Event() async def cb(_payment_hash): self.logger.debug(f"hold invoice callback called. {bob_w.network.get_local_height()=}") - nonlocal cb_got_called - cb_got_called = True + cb_got_called.set() bob_w.register_hold_invoice(lnaddr.paymenthash, cb) async def check_mpp_state(): - async def wait_for_resolution(): - while True: - await asyncio.sleep(0.1) - if payment_key not in bob_w.received_mpp_htlcs: - continue - if not bob_w.received_mpp_htlcs[payment_key].resolution == RecvMPPResolution.SETTLING: - continue - return - await util.wait_for2(wait_for_resolution(), timeout=2) - assert cb_got_called + await util.wait_for2(cb_got_called.wait(), timeout=2) mpp_set = bob_w.received_mpp_htlcs[payment_key] self.assertEqual(mpp_set.resolution, RecvMPPResolution.SETTLING, msg=mpp_set.resolution) self.assertEqual(len(mpp_set.htlcs), 1, f"should get only one htlc: {mpp_set.htlcs=}")