add tests fix interface
This commit is contained in:
dni ⚡
2025-01-08 09:39:18 +01:00
parent 5c688e08c9
commit 5210693440
3 changed files with 126 additions and 51 deletions

View File

@@ -29,6 +29,7 @@ from lnbits.wallets.base import (
PaymentResponse, PaymentResponse,
PaymentStatus, PaymentStatus,
PaymentSuccessStatus, PaymentSuccessStatus,
UnsupportedError,
) )
from ..crud import ( from ..crud import (
@@ -965,17 +966,15 @@ async def create_hold_invoice(
invoice_memo = None if description_hash else memo invoice_memo = None if description_hash else memo
funding_source = get_funding_source() funding_source = get_funding_source()
if funding_source.__class__.__name__ not in ["LndRestWallet", "LndWallet"]: try:
raise InvoiceError( res = await funding_source.create_hold_invoice(
"Hold invoices are only supported with LND.", status="failed" amount=amount,
memo=invoice_memo,
rhash=rhash,
description_hash=description_hash,
) )
except UnsupportedError as exc:
res = await funding_source.create_hold_invoice( raise InvoiceError(str(exc), status="failed") from exc
amount=amount,
memo=invoice_memo,
rhash=rhash,
description_hash=description_hash,
)
if not res.ok: if not res.ok:
raise InvoiceError( raise InvoiceError(
@@ -1004,23 +1003,24 @@ async def create_hold_invoice(
) )
# TODO: should return payment
# TODO: update payment status to success
async def settle_hold_invoice( async def settle_hold_invoice(
*, *,
preimage: str, preimage: str,
) -> bool: ) -> bool:
if len(preimage) != 32: if len(bytes.fromhex(preimage)) != 32:
raise InvoiceError( raise InvoiceError(
"Invalid preimage length. Must be 32 bytes", "Invalid preimage length. Must be 32 bytes",
status="failed", status="failed",
) )
funding_source = get_funding_source() funding_source = get_funding_source()
if funding_source.__class__.__name__ not in ["LndRestWallet", "LndWallet"]:
raise InvoiceError(
"Hold invoices are only supported with LND.", status="failed"
)
response = await funding_source.settle_hold_invoice(preimage=preimage) try:
response = await funding_source.settle_hold_invoice(preimage=preimage)
except UnsupportedError as exc:
raise InvoiceError(str(exc), status="failed") from exc
if not response.ok: if not response.ok:
raise InvoiceError("Unexpected backend error.", status="failed") raise InvoiceError("Unexpected backend error.", status="failed")
@@ -1028,32 +1028,38 @@ async def settle_hold_invoice(
return True return True
async def cancel_hold_invoice(payment_hash: str) -> bool: async def cancel_hold_invoice(payment_hash: str) -> Payment:
payment = await get_standalone_payment(payment_hash, incoming=True)
if not payment:
raise InvoiceError("Payment not found.", status="failed")
funding_source = get_funding_source() funding_source = get_funding_source()
if funding_source.__class__.__name__ not in ["LndRestWallet", "LndWallet"]: try:
raise InvoiceError( response = await funding_source.cancel_hold_invoice(payment_hash=payment_hash)
"Hold invoices are only supported with LND.", status="failed" except UnsupportedError as exc:
) raise InvoiceError(str(exc), status="failed") from exc
response = await funding_source.cancel_hold_invoice(payment_hash=payment_hash)
if not response.ok: if not response.ok:
raise InvoiceError("Unexpected backend error.", status="failed") raise InvoiceError(
response.error_message or "Unexpected backend error.", status="failed"
return True )
payment.status = PaymentState.FAILED
await update_payment(payment)
return payment
async def subscribe_hold_invoice(payment_hash: str) -> bool: async def subscribe_hold_invoice(payment_hash: str) -> bool:
payment = await get_standalone_payment(payment_hash, incoming=True) payment = await get_standalone_payment(payment_hash, incoming=True)
if not payment: if not payment:
raise InvoiceError("Payment not found.", status="failed") raise InvoiceError("Payment not found.", status="failed")
funding_source = get_funding_source() # funding_source = get_funding_source()
if funding_source.__class__.__name__ not in ["LndRestWallet", "LndWallet"]: try:
raise InvoiceError( # if payment.webhook:
"Hold invoices are only supported with LND.", status="failed" # asyncio. create_task(
) # funding_source.hold_invoices_stream(
# if payment.webhook: # payment_hash=payment_hash, webhook=payment.webhook
# asyncio. create_task( # )
# funding_source.hold_invoices_stream( # )
# payment_hash=payment_hash, webhook=payment.webhook pass
# ) except UnsupportedError as exc:
# ) raise InvoiceError(str(exc), status="failed") from exc
return True return True

View File

@@ -346,9 +346,12 @@ class LndRestWallet(Wallet):
else: else:
data["memo"] = memo or "" data["memo"] = memo or ""
r = await self.client.post(url="/v2/invoices/hodl", json=data) try:
r.raise_for_status() r = await self.client.post(url="/v2/invoices/hodl", json=data)
data = r.json() r.raise_for_status()
data = r.json()
except httpx.HTTPStatusError as exc:
return InvoiceResponse(False, None, None, exc.response.text)
payment_request = data["payment_request"] payment_request = data["payment_request"]
payment_hash = base64.b64encode(bytes.fromhex(rhash)).decode("ascii") payment_hash = base64.b64encode(bytes.fromhex(rhash)).decode("ascii")
@@ -357,20 +360,27 @@ class LndRestWallet(Wallet):
return InvoiceResponse(True, checking_id, payment_request, None) return InvoiceResponse(True, checking_id, payment_request, None)
async def settle_hold_invoice(self, preimage: str) -> PaymentResponse: async def settle_hold_invoice(self, preimage: str) -> PaymentResponse:
data: dict = {"preimage": base64.b64encode(preimage.encode()).decode("ascii")} data: dict = {
r = await self.client.post(url="/v2/invoices/settle", json=data) "preimage": base64.b64encode(bytes.fromhex(preimage)).decode("ascii")
r.raise_for_status() }
try:
return PaymentResponse(True, None, None, None, None) r = await self.client.post(url="/v2/invoices/settle", json=data)
r.raise_for_status()
return PaymentResponse(True, None, None, None, None)
except httpx.HTTPStatusError as exc:
return PaymentResponse(False, None, None, None, exc.response.text)
async def cancel_hold_invoice(self, payment_hash: str) -> PaymentResponse: async def cancel_hold_invoice(self, payment_hash: str) -> PaymentResponse:
data: dict = { rhash = bytes.fromhex(payment_hash)
"payment_hash": base64.b64encode(payment_hash.encode()).decode("ascii") try:
} r = await self.client.post(
r = await self.client.post(url="/v2/invoices/cancel", json=data) url="/v2/invoices/cancel",
r.raise_for_status() json={"payment_hash": base64.b64encode(rhash).decode("ascii")},
)
return PaymentResponse(True, None, None, None, None) r.raise_for_status()
return PaymentResponse(True, None, None, None, None)
except httpx.HTTPStatusError as exc:
return PaymentResponse(False, None, None, None, exc.response.text)
async def hold_invoices_stream(self, payment_hash: str, webhook: str): async def hold_invoices_stream(self, payment_hash: str, webhook: str):
try: try:

View File

@@ -0,0 +1,59 @@
import hashlib
import os
import pytest
from lnbits.core.services.payments import (
cancel_hold_invoice,
create_hold_invoice,
settle_hold_invoice,
)
from lnbits.exceptions import InvoiceError
from ..helpers import funding_source, is_fake
@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():
rhash = "0" * 32
with pytest.raises(InvoiceError):
await create_hold_invoice(
wallet_id="fake_wallet_id",
amount=1000,
memo="fake_holdinvoice",
rhash=rhash,
)
with pytest.raises(InvoiceError):
await settle_hold_invoice(preimage=rhash)
with pytest.raises(InvoiceError):
await cancel_hold_invoice(rhash)
@pytest.mark.anyio
@pytest.mark.skipif(is_fake, reason="this only works in regtest")
@pytest.mark.skipif(
funding_source.__class__.__name__ not in ["LndRestWallet"],
reason="this only works for lndrest",
)
async def test_pay_real_hold_invoice(from_wallet):
preimage = os.urandom(32)
preimage_hash = hashlib.sha256(preimage).hexdigest()
payment = await create_hold_invoice(
wallet_id=from_wallet.id,
amount=1000,
memo="test_holdinvoice",
rhash=preimage_hash,
)
assert payment.amount == 1000 * 1000
assert payment.memo == "test_holdinvoice"
assert payment.status == "pending"
assert payment.wallet_id == from_wallet.id
payment = await cancel_hold_invoice(payment_hash=preimage_hash)
assert payment.status == "failed"