Skip to content

Commit ae310ad

Browse files
committed
tests: add more lnurl testing for PaymentIdentifier
adds some more detailed tests to `test_payment_identifier.py` to test lnurlp and lnurlw separately and mock their resolve.
1 parent 4149589 commit ae310ad

File tree

1 file changed

+107
-5
lines changed

1 file changed

+107
-5
lines changed

tests/test_payment_identifier.py

Lines changed: 107 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import os
2+
import asyncio
3+
from unittest.mock import patch
24

35
from electrum import SimpleConfig
46
from electrum.invoices import Invoice
57
from electrum.payment_identifier import (maybe_extract_lightning_payment_identifier, PaymentIdentifier,
6-
PaymentIdentifierType, invoice_from_payment_identifier)
8+
PaymentIdentifierType, PaymentIdentifierState,
9+
invoice_from_payment_identifier)
710
from electrum.wallet import restore_wallet_from_text
11+
from electrum.lnurl import LNURL6Data, LNURL3Data, LNURLError
812

913
from . import ElectrumTestCase
1014
from electrum.transaction import PartialTxOutput
@@ -143,14 +147,112 @@ def test_bip21(self):
143147
pi = PaymentIdentifier(None, bip21)
144148
self.assertFalse(pi.is_valid())
145149

146-
def test_lnurl(self):
147-
lnurl = 'lnurl1dp68gurn8ghj7um9wfmxjcm99e5k7telwy7nxenrxvmrgdtzxsenjcm98pjnwxq96s9'
148-
pi = PaymentIdentifier(None, lnurl)
150+
def test_lnurl_basic(self):
151+
"""Test basic LNURL parsing without resolve"""
152+
valid_lnurl = 'lnurl1dp68gurn8ghj7um9wfmxjcm99e5k7telwy7nxenrxvmrgdtzxsenjcm98pjnwxq96s9'
153+
pi = PaymentIdentifier(None, valid_lnurl)
149154
self.assertTrue(pi.is_valid())
155+
self.assertEqual(PaymentIdentifierType.LNURL, pi.type)
150156
self.assertFalse(pi.is_available())
151157
self.assertTrue(pi.need_resolve())
158+
self.assertEqual(PaymentIdentifierState.NEED_RESOLVE, pi.state)
159+
160+
# Test with lightning: prefix
161+
lightning_lnurl = f'lightning:{valid_lnurl}'
162+
pi = PaymentIdentifier(None, lightning_lnurl)
163+
self.assertTrue(pi.is_valid())
164+
self.assertEqual(PaymentIdentifierType.LNURL, pi.type)
165+
self.assertTrue(pi.need_resolve())
166+
167+
@patch('electrum.payment_identifier.request_lnurl')
168+
def test_lnurl_pay_resolve(self, mock_request_lnurl):
169+
"""Test LNURL-pay (LNURL6) with mocked resolve"""
170+
valid_lnurl = 'LNURL1DP68GURN8GHJ7MRWVF5HGUEWD3HXZERYWFJHXUEWVDHK6TMVDE6HYMRS9ANRV46DXETQPJQCS4'
171+
172+
# Mock lnurl-p response
173+
mock_lnurl6_data = LNURL6Data(
174+
callback_url='https://example.com/lnurl-pay',
175+
max_sendable_sat=1_000_000,
176+
min_sendable_sat=1_000,
177+
metadata_plaintext='Test payment',
178+
comment_allowed=100,
179+
)
180+
mock_request_lnurl.return_value = mock_lnurl6_data
181+
182+
pi = PaymentIdentifier(None, valid_lnurl)
183+
self.assertTrue(pi.need_resolve())
184+
self.assertEqual(PaymentIdentifierType.LNURL, pi.type)
185+
186+
async def run_resolve():
187+
await pi._do_resolve()
188+
189+
asyncio.run(run_resolve())
190+
191+
self.assertEqual(PaymentIdentifierType.LNURLP, pi.type)
192+
self.assertEqual(PaymentIdentifierState.LNURLP_FINALIZE, pi.state)
193+
self.assertTrue(pi.need_finalize())
194+
self.assertIsNotNone(pi.lnurl_data)
195+
self.assertTrue(isinstance(pi.lnurl_data, LNURL6Data))
196+
self.assertEqual(1_000, pi.lnurl_data.min_sendable_sat)
197+
self.assertEqual(1_000_000, pi.lnurl_data.max_sendable_sat)
198+
self.assertEqual('Test payment', pi.lnurl_data.metadata_plaintext)
199+
self.assertEqual(100, pi.lnurl_data.comment_allowed)
200+
201+
@patch('electrum.payment_identifier.request_lnurl')
202+
def test_lnurl_withdraw_resolve(self, mock_request_lnurl):
203+
"""Test LNURL-withdraw (LNURL3) with mocked resolve"""
204+
valid_lnurl = 'LNURL1DP68GURN8GHJ7MRWVF5HGUEWD3HXZERYWFJHXUEWVDHK6TM4WPNHYCTYV4EJ7DFCVGENSDPH8QCRZETXVGCXGCMPVFJR' \
205+
'WENP8P3NJEP3XE3NQWRPXFJR2VRRVSCX2V33V5UNVC3SXP3RXCFSVFSKVWPCV3SKZWTP8YUZ7AMFW35XGUNPWUHKZURF9AMRZT' \
206+
'MVDE6HYMP0FETHVUNZDAMHQ7JSF4RX73TZ2VU9Z3J3GVMSLCJ57F'
207+
208+
# Mock lnurl-w response
209+
mock_lnurl3_data = LNURL3Data(
210+
callback_url='https://example.com/lnurl-withdraw',
211+
k1='test-k1-value',
212+
default_description='Test withdrawal',
213+
min_withdrawable_sat=1_000,
214+
max_withdrawable_sat=500_000,
215+
)
216+
mock_request_lnurl.return_value = mock_lnurl3_data
217+
218+
pi = PaymentIdentifier(None, valid_lnurl)
219+
self.assertTrue(pi.need_resolve())
220+
self.assertEqual(PaymentIdentifierType.LNURL, pi.type)
221+
222+
async def run_resolve():
223+
await pi._do_resolve()
224+
225+
asyncio.run(run_resolve())
226+
227+
self.assertEqual(PaymentIdentifierType.LNURLW, pi.type)
228+
self.assertEqual(PaymentIdentifierState.LNURLW_FINALIZE, pi.state)
229+
self.assertIsNotNone(pi.lnurl_data)
230+
self.assertEqual('test-k1-value', pi.lnurl_data.k1)
231+
self.assertEqual('Test withdrawal', pi.lnurl_data.default_description)
232+
self.assertEqual(1000, pi.lnurl_data.min_withdrawable_sat)
233+
self.assertEqual(500000, pi.lnurl_data.max_withdrawable_sat)
234+
235+
@patch('electrum.payment_identifier.request_lnurl')
236+
def test_lnurl_resolve_error(self, mock_request_lnurl):
237+
"""Test LNURL resolve error handling"""
238+
lnurl = 'LNURL1DP68GURN8GHJ7MRWVF5HGUEWD3HXZERYWFJHXUEWVDHK6TM4WPNHYCTYV4EJ7DFCVGENSDPH8QCRZETXVGCXGCMPVFJR' \
239+
'WENP8P3NJEP3XE3NQWRPXFJR2VRRVSCX2V33V5UNVC3SXP3RXCFSVFSKVWPCV3SKZWTP8YUZ7AMFW35XGUNPWUHKZURF9AMRZT' \
240+
'MVDE6HYMP0FETHVUNZDAMHQ7JSF4RX73TZ2VU9Z3J3GVMSLCJ57F'
241+
242+
# Mock LNURL error
243+
mock_request_lnurl.side_effect = LNURLError("Server error")
244+
245+
pi = PaymentIdentifier(None, lnurl)
246+
self.assertTrue(pi.need_resolve())
247+
248+
async def run_resolve():
249+
await pi._do_resolve()
250+
251+
asyncio.run(run_resolve())
152252

153-
# TODO: resolve mock
253+
self.assertEqual(PaymentIdentifierState.ERROR, pi.state)
254+
self.assertTrue(pi.is_error())
255+
self.assertIn("Server error", pi.get_error())
154256

155257
def test_multiline(self):
156258
pi_str = '\n'.join([

0 commit comments

Comments
 (0)