Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions tests/lightning/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import pytest


# Skip the global mint_server fixture for lightning-specific tests
@pytest.fixture
def mint_server():
yield None


@pytest.fixture(scope="session")
def mint():
yield None
139 changes: 139 additions & 0 deletions tests/lightning/test_clnrest_mpp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
from unittest.mock import AsyncMock, Mock, patch

import pytest
from bolt11 import Bolt11

from cashu.core.base import MeltQuote, MeltQuoteState, Unit
from cashu.lightning.base import PaymentResult
from cashu.lightning.clnrest import CLNRestWallet


def create_melt_quote(
amount: int, unit: str = "msat", request: str = "lnbc10u1p0example"
) -> MeltQuote:
return MeltQuote(
quote="quote_test_123",
method="bolt11",
request=request,
checking_id="checking_id_test",
unit=unit,
amount=amount,
fee_reserve=1000,
state=MeltQuoteState.unpaid,
created_time=None,
paid_time=None,
)


@pytest.fixture
def wallet(monkeypatch: pytest.MonkeyPatch) -> CLNRestWallet:
monkeypatch.setattr(
"cashu.lightning.clnrest.settings.mint_clnrest_url", "https://localhost:3010"
)
monkeypatch.setattr(
"cashu.lightning.clnrest.settings.mint_clnrest_rune", "test_rune"
)
monkeypatch.setattr("cashu.lightning.clnrest.settings.mint_clnrest_cert", False)
monkeypatch.setattr(
"cashu.lightning.clnrest.settings.mint_clnrest_enable_mpp", True
)
monkeypatch.setattr(
"cashu.lightning.clnrest.settings.mint_retry_exponential_backoff_base_delay", 1
)
monkeypatch.setattr(
"cashu.lightning.clnrest.settings.mint_retry_exponential_backoff_max_delay", 10
)

mock_client = Mock()
mock_client.post = AsyncMock()
monkeypatch.setattr(
"cashu.lightning.clnrest.httpx.AsyncClient", Mock(return_value=mock_client)
)

wallet = CLNRestWallet(unit=Unit.sat)
wallet.supports_mpp = True
return wallet


@pytest.mark.asyncio
async def test_mpp_detection_routes_to_partial(wallet: CLNRestWallet):
with patch("cashu.lightning.clnrest.decode") as mock_decode:
mock_invoice = Mock(spec=Bolt11)
mock_invoice.payment_hash = "hash789"
mock_invoice.amount_msat = 1000000
mock_decode.return_value = mock_invoice

wallet.client.post = AsyncMock(
return_value=Mock(
is_error=False,
json=lambda: {
"payment_hash": "hash789",
"payment_preimage": "preimage_mpp",
"amount_sent_msat": 600100,
"amount_msat": 600000,
"status": "complete",
},
)
)

quote = create_melt_quote(amount=600000, unit="msat")
fee_limit_msat = 1000

result = await wallet.pay_invoice(quote, fee_limit_msat)

assert result.result == PaymentResult.SETTLED
assert result.preimage == "preimage_mpp"
call_data = wallet.client.post.call_args.kwargs["data"]
assert "partial_msat" in call_data, "partial_msat must be sent to CLN for MPP"
assert call_data["partial_msat"] == 600000


@pytest.mark.asyncio
async def test_mpp_disabled_returns_error(wallet: CLNRestWallet):
wallet.supports_mpp = False

with patch("cashu.lightning.clnrest.decode") as mock_decode:
mock_invoice = Mock(spec=Bolt11)
mock_invoice.payment_hash = "hash123"
mock_invoice.amount_msat = 1000000
mock_decode.return_value = mock_invoice

quote = create_melt_quote(amount=600000, unit="msat")

result = await wallet.pay_invoice(quote, 1000)

assert result.result == PaymentResult.FAILED
assert result.error_message is not None
assert "does not support MPP" in result.error_message


@pytest.mark.asyncio
async def test_full_payment_no_mpp(wallet: CLNRestWallet):
with patch("cashu.lightning.clnrest.decode") as mock_decode:
mock_invoice = Mock(spec=Bolt11)
mock_invoice.payment_hash = "full_payment_hash"
mock_invoice.amount_msat = 1000000
mock_decode.return_value = mock_invoice

quote = create_melt_quote(amount=1000000, unit="msat")

wallet.client.post = AsyncMock(
return_value=Mock(
is_error=False,
json=lambda: {
"payment_hash": "full_payment_hash",
"payment_preimage": "preimage123",
"amount_sent_msat": 1000100,
"amount_msat": 1000000,
"status": "complete",
},
)
)

result = await wallet.pay_invoice(quote, 1000)

assert result.result == PaymentResult.SETTLED
assert result.preimage == "preimage123"
call_data = wallet.client.post.call_args.kwargs["data"]
assert "partial_msat" not in call_data, "partial_msat must not be sent for full payments"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nicely done on trimming the PR. Looking at the existing tests though, there's already an established pattern for testing Lightning backends in tests/lightning/test_lightning_backends_mocked.py, including a test for CLNRest without MPP support.
These new files duplicate some of the tests there with a different approach as well.
Could you have a look at the existing file and follow the pattern there to avoid fragmenting the testing convention?