pytest: test xpay notifications.

The custom_notifications handler produces really ugly results, and I
was lazy, but it works!

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2025-08-14 11:48:05 +09:30
parent e24bd9685a
commit c88ec27de3
2 changed files with 60 additions and 0 deletions

View File

@@ -34,6 +34,16 @@ def on_pay_success(origin, payload, **kwargs):
)
@plugin.subscribe("pay_part_start")
def on_pay_part_start(origin, payload, **kwargs):
plugin.log("Got pay_part_start: {}".format(payload))
@plugin.subscribe("pay_part_end")
def on_pay_part_end(origin, payload, **kwargs):
plugin.log("Got pay_part_end: {}".format(payload))
@plugin.subscribe("ididntannouncethis")
def on_faulty_emit(origin, payload, **kwargs):
"""We should never receive this as it gets dropped.

View File

@@ -832,3 +832,53 @@ lightning-cli pay lni1qqgv5nalmz08ukj4av074kyk6pepq93pqvvhnlnvurnfanndnxjtcjnmxr
# This doesn't!
l1.rpc.xpay(inv)
l1.daemon.wait_for_log(f'Adding HTLC 1 amount=15002msat cltv={110 + 1 + 100 + 200 + 400}')
def test_attempt_notifications(node_factory):
plugin_path = os.path.join(os.getcwd(), 'tests/plugins/custom_notifications.py')
l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True,
opts=[{"plugin": plugin_path}, {}, {}])
scid12 = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['short_channel_id']
scid12_dir = only_one(l1.rpc.listpeerchannels(l2.info['id'])['channels'])['direction']
scid23 = only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['short_channel_id']
scid23_dir = only_one(l2.rpc.listpeerchannels(l3.info['id'])['channels'])['direction']
inv1 = l3.rpc.invoice(5000000, 'test_attempt_notifications1', 'test_attempt_notifications1')
l1.rpc.xpay(inv1['bolt11'])
line = l1.daemon.wait_for_log("plugin-custom_notifications.py: Got pay_part_start: ")
regex = r".*Got pay_part_start: \{'payment_hash': '" + inv1['payment_hash'] + r"', 'groupid': [0-9]*, 'partid': 1, 'total_payment_msat': 5000000, 'attempt_msat': 5000000, 'hops': \[\{'next_node': '" + l2.info['id'] + r"', 'short_channel_id': '" + scid12 + r"', 'direction': " + str(scid12_dir) + r", 'channel_in_msat': 5000051, 'channel_out_msat': 5000051\}, \{'next_node': '" + l3.info['id'] + r"', 'short_channel_id': '" + scid23 + r"', 'direction': " + str(scid23_dir) + r", 'channel_in_msat': 5000051, 'channel_out_msat': 5000000\}\]\}"
assert re.match(regex, line)
# Note, duration always has 9 decimals, EXCEPT that the python code interprets it, so if the last digit is a 0 it will only print 8.
line = l1.daemon.wait_for_log("plugin-custom_notifications.py: Got pay_part_end: ")
regex = r".*Got pay_part_end: \{'status': 'success', 'duration': [0-9]*\.[0-9]*, 'payment_hash': '" + inv1['payment_hash'] + r"', 'groupid': [0-9]*, 'partid': 1\}"
assert re.match(regex, line)
inv2 = l3.rpc.invoice(10000000, 'test_attempt_notifications2', 'test_attempt_notifications2')
l3.rpc.delinvoice('test_attempt_notifications2', "unpaid")
# Final node failure
with pytest.raises(RpcError, match=r"Destination said it doesn't know invoice: incorrect_or_unknown_payment_details"):
l1.rpc.xpay(inv2['bolt11'])
line = l1.daemon.wait_for_log("plugin-custom_notifications.py: Got pay_part_start: ")
regex = r".*Got pay_part_start: \{'payment_hash': '" + inv2['payment_hash'] + r"', 'groupid': [0-9]*, 'partid': 1, 'total_payment_msat': 10000000, 'attempt_msat': 10000000, 'hops': \[\{'next_node': '" + l2.info['id'] + r"', 'short_channel_id': '" + scid12 + r"', 'direction': " + str(scid12_dir) + r", 'channel_in_msat': 10000101, 'channel_out_msat': 10000101\}, \{'next_node': '" + l3.info['id'] + r"', 'short_channel_id': '" + scid23 + r"', 'direction': " + str(scid23_dir) + r", 'channel_in_msat': 10000101, 'channel_out_msat': 10000000\}\]\}"
assert re.match(regex, line)
line = l1.daemon.wait_for_log("plugin-custom_notifications.py: Got pay_part_end: ")
regex = r".*Got pay_part_end: \{'status': 'failure', 'payment_hash': '" + inv2['payment_hash'] + r"', 'groupid': [0-9]*, 'partid': 1, 'failed_msg': '400f00000000009896800000006c', 'duration': [0-9]*\.[0-9]*, 'failed_node_id': '" + l3.info['id'] + r"', 'error_code': 16399, 'error_message': 'incorrect_or_unknown_payment_details'\}"
assert re.match(regex, line)
# Intermediary node failure
l3.stop()
with pytest.raises(RpcError, match=r"Failed after 1 attempts"):
l1.rpc.xpay(inv2['bolt11'])
line = l1.daemon.wait_for_log("plugin-custom_notifications.py: Got pay_part_start: ")
regex = r".*Got pay_part_start: \{'payment_hash': '" + inv2['payment_hash'] + r"', 'groupid': [0-9]*, 'partid': 1, 'total_payment_msat': 10000000, 'attempt_msat': 10000000, 'hops': \[\{'next_node': '" + l2.info['id'] + r"', 'short_channel_id': '" + scid12 + r"', 'direction': " + str(scid12_dir) + r", 'channel_in_msat': 10000101, 'channel_out_msat': 10000101\}, \{'next_node': '" + l3.info['id'] + r"', 'short_channel_id': '" + scid23 + r"', 'direction': " + str(scid23_dir) + r", 'channel_in_msat': 10000101, 'channel_out_msat': 10000000\}\]\}"
assert re.match(regex, line)
line = l1.daemon.wait_for_log("plugin-custom_notifications.py: Got pay_part_end: ")
regex = r".*Got pay_part_end: \{'status': 'failure', 'payment_hash': '" + inv2['payment_hash'] + r"', 'groupid': [0-9]*, 'partid': 1, 'failed_msg': '1007[a-f0-9]*', 'duration': [0-9]*\.[0-9]*, 'failed_node_id': '" + l2.info['id'] + r"', 'failed_short_channel_id': '" + scid23 + r"', 'failed_direction': " + str(scid23_dir) + r", 'error_code': 4103, 'error_message': 'temporary_channel_failure'\}"
assert re.match(regex, line)