feat: HodlInvoices (Rebase) (#2869)

Co-authored-by: pseudozach <git@pseudozach.com>
Co-authored-by: dni  <office@dnilabs.com>
Co-authored-by: Vlad Stan <stan.v.vlad@gmail.com>
This commit is contained in:
ziggieXXX 2025-07-10 16:11:08 +02:00 committed by dni ⚡
parent 6e9f451419
commit f91c933919
No known key found for this signature in database
GPG key ID: D1F416F29AD26E87
23 changed files with 4192 additions and 6027 deletions

View file

@ -0,0 +1,118 @@
import asyncio
import pytest
from lnbits.core.models import Payment
from lnbits.core.services.payments import (
cancel_hold_invoice,
create_invoice,
get_standalone_payment,
settle_hold_invoice,
)
from lnbits.exceptions import InvoiceError
from lnbits.utils.crypto import random_secret_and_hash
from ..helpers import funding_source, is_fake
from .helpers import pay_real_invoice
@pytest.mark.anyio
@pytest.mark.skipif(is_fake, reason="this only works in regtest")
@pytest.mark.skipif(
funding_source.__class__.__name__ in ["LndRestWallet", "LndWallet"],
reason="this should not raise for lnd",
)
async def test_pay_raise_unsupported(app):
payment_hash = "0" * 32
payment = Payment(
checking_id=payment_hash,
amount=1000,
wallet_id="fake_wallet_id",
bolt11="fake_holdinvoice",
payment_hash=payment_hash,
fee=0,
)
with pytest.raises(InvoiceError):
await create_invoice(
wallet_id="fake_wallet_id",
amount=1000,
memo="fake_holdinvoice",
payment_hash=payment_hash,
)
with pytest.raises(InvoiceError):
await settle_hold_invoice(payment, payment_hash)
with pytest.raises(InvoiceError):
await cancel_hold_invoice(payment)
@pytest.mark.anyio
@pytest.mark.skipif(is_fake, reason="this only works in regtest")
@pytest.mark.skipif(
funding_source.__class__.__name__ not in ["LndRestWallet", "LndWallet"],
reason="this only works for lndrest",
)
async def test_cancel_real_hold_invoice(app, from_wallet):
_, payment_hash = random_secret_and_hash()
payment = await create_invoice(
wallet_id=from_wallet.id,
amount=1000,
memo="test_cancel_holdinvoice",
payment_hash=payment_hash,
)
assert payment.amount == 1000 * 1000
assert payment.memo == "test_cancel_holdinvoice"
assert payment.status == "pending"
assert payment.wallet_id == from_wallet.id
payment = await cancel_hold_invoice(payment=payment)
assert payment.ok is True
updated_payment = await get_standalone_payment(payment_hash, incoming=True)
assert updated_payment
assert updated_payment.status == "failed"
@pytest.mark.anyio
@pytest.mark.skipif(is_fake, reason="this only works in regtest")
@pytest.mark.skipif(
funding_source.__class__.__name__ not in ["LndRestWallet", "LndWallet"],
reason="this only works for lndrest",
)
async def test_settle_real_hold_invoice(app, from_wallet):
preimage, payment_hash = random_secret_and_hash()
payment = await create_invoice(
wallet_id=from_wallet.id,
amount=1000,
memo="test_settle_holdinvoice",
payment_hash=payment_hash,
)
assert payment.amount == 1000 * 1000
assert payment.memo == "test_settle_holdinvoice"
assert payment.status == "pending"
assert payment.wallet_id == from_wallet.id
# invoice should still be open
with pytest.raises(InvoiceError):
await settle_hold_invoice(payment=payment, preimage=preimage)
def pay_invoice():
pay_real_invoice(payment.bolt11)
async def settle():
await asyncio.sleep(1)
invoice_response = await settle_hold_invoice(payment=payment, preimage=preimage)
assert invoice_response.ok is True
coro = asyncio.to_thread(pay_invoice)
task = asyncio.create_task(coro)
await asyncio.gather(task, settle())
await asyncio.sleep(1)
updated_payment = await get_standalone_payment(payment_hash, incoming=True)
assert updated_payment
assert updated_payment.status == "success"