From 1df9fa831d177cb443f03f04968a7499dd981a66 Mon Sep 17 00:00:00 2001 From: Vlad Stan Date: Mon, 9 Mar 2026 11:23:31 +0200 Subject: [PATCH] fix: lint --- tests/api/test_asset_api.py | 4 +- tests/api/test_auth_api.py | 6 +- tests/api/test_callback_api.py | 4 +- tests/api/test_extension_api.py | 18 +++-- tests/api/test_extensions_builder_api.py | 5 +- tests/api/test_fiat_api.py | 7 +- tests/api/test_lnurl_api.py | 16 +++- tests/api/test_node_api.py | 93 ++++++++++++++---------- tests/api/test_payment_api.py | 2 +- tests/api/test_user_api.py | 4 +- tests/api/test_wallet_api.py | 12 ++- 11 files changed, 106 insertions(+), 65 deletions(-) diff --git a/tests/api/test_asset_api.py b/tests/api/test_asset_api.py index b68e05308..b6667d45e 100644 --- a/tests/api/test_asset_api.py +++ b/tests/api/test_asset_api.py @@ -85,7 +85,9 @@ async def test_asset_api_upload_list_update_and_delete( assert deleted.status_code == 200 assert deleted.json()["success"] is True - missing = await client.get(f"/api/v1/assets/{asset['id']}", headers=user_headers_from) + missing = await client.get( + f"/api/v1/assets/{asset['id']}", headers=user_headers_from + ) assert missing.status_code == 404 diff --git a/tests/api/test_auth_api.py b/tests/api/test_auth_api.py index bbd635bd3..416c91940 100644 --- a/tests/api/test_auth_api.py +++ b/tests/api/test_auth_api.py @@ -6,9 +6,9 @@ from fastapi.responses import RedirectResponse from httpx import AsyncClient from lnbits.core.crud.users import get_account, update_account -from lnbits.core.views.auth_api import get_account_by_email from lnbits.core.models.users import Account from lnbits.core.services.users import create_user_account +from lnbits.core.views.auth_api import get_account_by_email from lnbits.settings import Settings @@ -59,9 +59,7 @@ async def test_auth_api_logout_and_update_ui_customization( @pytest.mark.anyio -async def test_auth_api_sso_login_and_callback( - http_client: AsyncClient, mocker -): +async def test_auth_api_sso_login_and_callback(http_client: AsyncClient, mocker): user = await create_user_account( Account( id=uuid4().hex, diff --git a/tests/api/test_callback_api.py b/tests/api/test_callback_api.py index d45a6ba82..afe5227c9 100644 --- a/tests/api/test_callback_api.py +++ b/tests/api/test_callback_api.py @@ -163,7 +163,9 @@ async def test_callback_api_handles_subscription_flows_and_validation(mocker): ) assert create_fiat_invoice_mock.await_count == 2 - with pytest.raises(ValueError, match="PayPal subscription event missing custom metadata."): + with pytest.raises( + ValueError, match="PayPal subscription event missing custom metadata." + ): await handle_paypal_event( { "id": "evt_bad_sale", diff --git a/tests/api/test_extension_api.py b/tests/api/test_extension_api.py index e053676e1..9abb886ad 100644 --- a/tests/api/test_extension_api.py +++ b/tests/api/test_extension_api.py @@ -220,7 +220,9 @@ async def test_extension_api_pay_to_enable_and_catalog_views(mocker, admin_user) await create_installed_extension( _installable_extension( ext_id, - pay_to_enable=PayToEnableInfo(required=True, amount=10, wallet=admin_wallet.id), + pay_to_enable=PayToEnableInfo( + required=True, amount=10, wallet=admin_wallet.id + ), ) ) @@ -410,6 +412,10 @@ async def test_extension_api_activate_uninstall_install_invoice_and_cleanup(mock @pytest.mark.anyio async def test_extension_api_review_endpoints(mocker): ext_id = f"review_{uuid4().hex[:8]}" + reviews_base = "https://demo.lnbits.com/paidreviews/api/v1/AdFzLjzuKFLsdk4Bcnff6r" + tags_url = f"{reviews_base}/tags" + reviews_url = f"{reviews_base}/reviews/{ext_id}?offset=0&limit=5" + create_review_url = f"{reviews_base}/reviews" request = Request( { "type": "http", @@ -421,10 +427,10 @@ async def test_extension_api_review_endpoints(mocker): ) mock_client = _MockHTTPClient( { - "https://demo.lnbits.com/paidreviews/api/v1/AdFzLjzuKFLsdk4Bcnff6r/tags": _MockHTTPResponse( + tags_url: _MockHTTPResponse( json_data=[{"tag": "good", "avg_rating": 900, "review_count": 3}] ), - f"https://demo.lnbits.com/paidreviews/api/v1/AdFzLjzuKFLsdk4Bcnff6r/reviews/{ext_id}?offset=0&limit=5": _MockHTTPResponse( + reviews_url: _MockHTTPResponse( json_data={ "data": [ { @@ -438,7 +444,7 @@ async def test_extension_api_review_endpoints(mocker): "total": 1, } ), - "https://demo.lnbits.com/paidreviews/api/v1/AdFzLjzuKFLsdk4Bcnff6r/reviews": _MockHTTPResponse( + create_review_url: _MockHTTPResponse( json_data={ "payment_hash": f"hash_{uuid4().hex[:8]}", "payment_request": "lnbc1review", @@ -446,7 +452,9 @@ async def test_extension_api_review_endpoints(mocker): ), } ) - mocker.patch("lnbits.core.views.extension_api.httpx.AsyncClient", return_value=mock_client) + mocker.patch( + "lnbits.core.views.extension_api.httpx.AsyncClient", return_value=mock_client + ) tags = await get_extension_reviews_tags() assert tags[0].tag == "good" diff --git a/tests/api/test_extensions_builder_api.py b/tests/api/test_extensions_builder_api.py index f07bb4377..d81912d77 100644 --- a/tests/api/test_extensions_builder_api.py +++ b/tests/api/test_extensions_builder_api.py @@ -7,7 +7,6 @@ from lnbits.core.crud.extensions import create_user_extension, get_user_extensio from lnbits.core.crud.users import get_account from lnbits.core.models.extensions import ( Extension, - ExtensionMeta, ExtensionRelease, UserExtension, ) @@ -81,7 +80,9 @@ async def test_extensions_builder_api_build_preview_and_cleanup( "lnbits.core.views.extensions_builder_api.build_extension_from_data", mocker.AsyncMock(return_value=(_release(ext_id), build_dir)), ) - clean_mock = mocker.patch("lnbits.core.views.extensions_builder_api.clean_extension_builder_data") + clean_mock = mocker.patch( + "lnbits.core.views.extensions_builder_api.clean_extension_builder_data" + ) try: settings.lnbits_data_folder = str(tmp_path) diff --git a/tests/api/test_fiat_api.py b/tests/api/test_fiat_api.py index a673ba93b..03cdd2143 100644 --- a/tests/api/test_fiat_api.py +++ b/tests/api/test_fiat_api.py @@ -1,5 +1,3 @@ -from typing import Any - import pytest from httpx import AsyncClient from pytest_mock.plugin import MockerFixture @@ -9,8 +7,8 @@ from lnbits.fiat.base import FiatSubscriptionResponse class FakeStripeWallet: - def __init__(self, secret: str | None = "secret"): - self._secret = secret + def __init__(self, secret: str | None = None): + self._secret = secret if secret is not None else "connection-token" async def create_terminal_connection_token(self) -> dict[str, str]: if self._secret is None: @@ -77,6 +75,7 @@ async def test_fiat_api_test_provider_and_subscription_lifecycle( assert created.status_code == 200 assert created.json()["checkout_session_url"] == "https://stripe.example/checkout" provider.create_subscription.assert_awaited_once() + assert provider.create_subscription.await_args is not None assert provider.create_subscription.await_args.args[2].wallet_id == from_wallet.id cancelled = await client.delete( diff --git a/tests/api/test_lnurl_api.py b/tests/api/test_lnurl_api.py index 92cbd41ff..5099e39cb 100644 --- a/tests/api/test_lnurl_api.py +++ b/tests/api/test_lnurl_api.py @@ -20,13 +20,13 @@ from lnbits.core.models import Account, CreateInvoice from lnbits.core.models.lnurl import CreateLnurlPayment, LnurlScan from lnbits.core.models.wallets import KeyType, WalletTypeInfo from lnbits.core.services.payments import create_wallet_invoice +from lnbits.core.services.users import create_user_account from lnbits.core.views.lnurl_api import ( api_lnurlscan, api_lnurlscan_post, api_payments_pay_lnurl, api_perform_lnurlauth, ) -from lnbits.core.services.users import create_user_account TEST_BOLT11 = ( "lnbc1pnsu5z3pp57getmdaxhg5kc9yh2a2qsh7cjf4gnccgkw0qenm8vsqv50w7s" @@ -57,9 +57,13 @@ async def test_lnurl_api_scan_routes_validate_and_forward(mocker): ) scanned = await api_lnurlscan("lnurl1example") + assert isinstance(scanned, LnurlPayResponse) assert scanned.callback == pay_response.callback - scanned_post = await api_lnurlscan_post(scan=LnurlScan(lnurl=LnAddress("alice@example.com"))) + scanned_post = await api_lnurlscan_post( + scan=LnurlScan(lnurl=LnAddress("alice@example.com")) + ) + assert isinstance(scanned_post, LnurlPayResponse) assert scanned_post.callback == pay_response.callback mocker.patch( @@ -102,6 +106,7 @@ async def test_lnurl_api_auth_and_pay_flow(mocker): mocker.AsyncMock(return_value=auth_response), ) authenticated = await api_perform_lnurlauth(auth_response, wallet_info) + assert isinstance(authenticated, LnurlAuthResponse) assert authenticated.k1 == "k1-value" mocker.patch( @@ -114,7 +119,7 @@ async def test_lnurl_api_auth_and_pay_flow(mocker): action_response = LnurlPayActionResponse( pr=LightningInvoice(TEST_BOLT11), disposable=False, - successAction=MessageAction(message="paid"), + successAction=parse_obj_as(MessageAction, {"message": "paid"}), ) fetch_mock = mocker.patch( "lnbits.core.views.lnurl_api.fetch_lnurl_pay_request", @@ -126,13 +131,16 @@ async def test_lnurl_api_auth_and_pay_flow(mocker): ) paid = await api_payments_pay_lnurl( - CreateLnurlPayment(res=pay_response, amount=2_000, unit="USD", comment="thanks"), + CreateLnurlPayment( + res=pay_response, amount=2_000, unit="USD", comment="thanks" + ), wallet_info, ) assert paid.payment_hash == payment.payment_hash fetch_mock.assert_awaited_once() pay_mock.assert_awaited_once() assert pay_mock.await_args is not None + assert action_response.successAction is not None assert pay_mock.await_args.kwargs["extra"] == { "stored": True, "success_action": action_response.successAction.json(), diff --git a/tests/api/test_node_api.py b/tests/api/test_node_api.py index 8c78f2581..9e163d278 100644 --- a/tests/api/test_node_api.py +++ b/tests/api/test_node_api.py @@ -1,9 +1,10 @@ -from typing import Any +from collections.abc import Callable +from typing import Any, cast from uuid import uuid4 import httpx import pytest -from httpx import AsyncClient +from fastapi import HTTPException from pytest_mock.plugin import MockerFixture from lnbits.core.views import node_api @@ -12,6 +13,8 @@ from lnbits.nodes.base import ( ChannelBalance, ChannelPoint, ChannelState, + ChannelStats, + Node, NodeChannel, NodeFees, NodeInfoResponse, @@ -46,13 +49,13 @@ class FakeNode: color="#ffffff", num_peers=1, blockheight=1, - channel_stats={ - "counts": {ChannelState.ACTIVE: 1}, - "avg_size": 3000, - "biggest_size": 3000, - "smallest_size": 3000, - "total_capacity": 3000, - }, + channel_stats=ChannelStats( + counts={ChannelState.ACTIVE: 1}, + avg_size=3000, + biggest_size=3000, + smallest_size=3000, + total_capacity=3000, + ), addresses=["127.0.0.1:9735"], onchain_balance_sat=1, onchain_confirmed_sat=1, @@ -146,8 +149,20 @@ class FakeNode: return "fake-node-id" +class FakeFundingSource: + def __init__( + self, + features: list[Feature], + node_factory: Callable[[Any], Any] | None, + ): + self.features = features + self.__node_cls__ = node_factory + + class MockHTTPResponse: - def __init__(self, json_data: dict[str, Any], status_error: Exception | None = None): + def __init__( + self, json_data: dict[str, Any], status_error: Exception | None = None + ): self._json_data = json_data self._status_error = status_error @@ -181,26 +196,24 @@ async def test_node_api_dependency_guards(settings: Settings, mocker: MockerFixt original_public = settings.lnbits_public_node_ui try: settings.lnbits_node_ui = True - funding_source = type("FundingSource", (), {})() - funding_source.features = [] - funding_source.__node_cls__ = None + funding_source = FakeFundingSource([], None) mocker.patch( "lnbits.core.views.node_api.get_funding_source", return_value=funding_source, ) - with pytest.raises(Exception) as excinfo: + with pytest.raises(HTTPException) as excinfo: node_api.require_node() assert excinfo.value.status_code == 501 - node_enabled_source = type("FundingSource", (), {})() - node_enabled_source.features = [Feature.nodemanager] - node_enabled_source.__node_cls__ = lambda wallet: "fake-node" + node_enabled_source = FakeFundingSource( + [Feature.nodemanager], lambda wallet: "fake-node" + ) mocker.patch( "lnbits.core.views.node_api.get_funding_source", return_value=node_enabled_source, ) settings.lnbits_node_ui = False - with pytest.raises(Exception) as disabled: + with pytest.raises(HTTPException) as disabled: node_api.require_node() assert disabled.value.status_code == 503 @@ -208,7 +221,7 @@ async def test_node_api_dependency_guards(settings: Settings, mocker: MockerFixt assert node_api.require_node() == "fake-node" settings.lnbits_public_node_ui = False - with pytest.raises(Exception) as public_disabled: + with pytest.raises(HTTPException) as public_disabled: node_api.check_public() assert public_disabled.value.status_code == 503 finally: @@ -222,6 +235,7 @@ async def test_node_api_route_functions_with_fake_node( mocker: MockerFixture, ): fake_node = FakeNode() + node = cast(Node, fake_node) original_transactions = settings.lnbits_node_ui_transactions settings.lnbits_node_ui_transactions = True rank_response = MockHTTPResponse( @@ -243,23 +257,23 @@ async def test_node_api_route_functions_with_fake_node( try: assert await node_api.api_get_ok() is None - public_info = await node_api.api_get_public_info(node=fake_node) + public_info = await node_api.api_get_public_info(node=node) assert public_info.backend_name == "FakeNode" - info = await node_api.api_get_info(node=fake_node) + info = await node_api.api_get_info(node=node) assert info is not None assert info.id == "node-id" - channels = await node_api.api_get_channels(node=fake_node) + channels = await node_api.api_get_channels(node=node) assert channels is not None assert channels[0].id == "chan-1" - channel = await node_api.api_get_channel("chan-1", node=fake_node) + channel = await node_api.api_get_channel("chan-1", node=node) assert channel is not None assert channel.peer_id == "peer-1" created = await node_api.api_create_channel( - node=fake_node, + node=node, peer_id="peer-1", funding_amount=10_000, push_amount=100, @@ -272,41 +286,41 @@ async def test_node_api_route_functions_with_fake_node( funding_txid=None, output_index=None, force=True, - node=fake_node, + node=node, ) assert deleted is not None assert deleted[0].id == "chan-1" await node_api.api_set_channel_fees( "chan-1", - node=fake_node, + node=node, fee_ppm=42, fee_base_msat=7, ) assert fake_node.fees_updated == ("chan-1", 7, 42) - payments = await node_api.api_get_payments(node=fake_node, filters=Filters()) + payments = await node_api.api_get_payments(node=node, filters=Filters()) assert payments is not None assert payments.total == 1 - invoices = await node_api.api_get_invoices(node=fake_node, filters=Filters()) + invoices = await node_api.api_get_invoices(node=node, filters=Filters()) assert invoices is not None assert invoices.total == 1 - peers = await node_api.api_get_peers(node=fake_node) + peers = await node_api.api_get_peers(node=node) assert peers[0].id == "peer-1" connect = await node_api.api_connect_peer( - uri="peer-1@127.0.0.1:9735", node=fake_node + uri="peer-1@127.0.0.1:9735", node=node ) assert connect["uri"] == "peer-1@127.0.0.1:9735" - disconnect = await node_api.api_disconnect_peer("peer-1", node=fake_node) + disconnect = await node_api.api_disconnect_peer("peer-1", node=node) assert disconnect["peer_id"] == "peer-1" - rank = await node_api.api_get_1ml_stats(node=fake_node) + rank = await node_api.api_get_1ml_stats(node=node) assert rank is not None - assert rank["channelcount"] == 2 + assert rank.channelcount == 2 finally: settings.lnbits_node_ui_transactions = original_transactions @@ -317,6 +331,7 @@ async def test_node_api_transactions_and_rank_errors( mocker: MockerFixture, ): fake_node = FakeNode() + node = cast(Node, fake_node) original_transactions = settings.lnbits_node_ui_transactions settings.lnbits_node_ui_transactions = False @@ -334,16 +349,16 @@ async def test_node_api_transactions_and_rank_errors( ) try: - with pytest.raises(Exception) as payments: - await node_api.api_get_payments(node=fake_node, filters=Filters()) + with pytest.raises(HTTPException) as payments: + await node_api.api_get_payments(node=node, filters=Filters()) assert payments.value.status_code == 503 - with pytest.raises(Exception) as invoices: - await node_api.api_get_invoices(node=fake_node, filters=Filters()) + with pytest.raises(HTTPException) as invoices: + await node_api.api_get_invoices(node=node, filters=Filters()) assert invoices.value.status_code == 503 - with pytest.raises(Exception) as rank: - await node_api.api_get_1ml_stats(node=fake_node) + with pytest.raises(HTTPException) as rank: + await node_api.api_get_1ml_stats(node=node) assert rank.value.status_code == 404 assert rank.value.detail == "Node not found on 1ml.com" finally: diff --git a/tests/api/test_payment_api.py b/tests/api/test_payment_api.py index d4f7eab21..2b5646848 100644 --- a/tests/api/test_payment_api.py +++ b/tests/api/test_payment_api.py @@ -11,6 +11,7 @@ from lnbits.core.models.payments import CancelInvoice, CreatePayment, SettleInvo from lnbits.core.models.users import AccountId from lnbits.core.models.wallets import KeyType, WalletTypeInfo from lnbits.core.services.payments import create_wallet_invoice +from lnbits.core.services.users import create_user_account from lnbits.core.views.payment_api import ( api_all_payments_paginated, api_payments_cancel, @@ -20,7 +21,6 @@ from lnbits.core.views.payment_api import ( api_payments_settle, api_payments_wallets_stats, ) -from lnbits.core.services.users import create_user_account from lnbits.db import Filters from lnbits.wallets.base import InvoiceResponse diff --git a/tests/api/test_user_api.py b/tests/api/test_user_api.py index ad0c5ce67..e181b04c3 100644 --- a/tests/api/test_user_api.py +++ b/tests/api/test_user_api.py @@ -96,5 +96,7 @@ async def test_user_api_create_wallet_validates_currency(): with pytest.raises(ValueError, match="Currency 'INVALID' not allowed."): await api_users_create_user_wallet(user.id, name="invalid", currency="INVALID") - wallet = await api_users_create_user_wallet(user.id, name="eur wallet", currency="EUR") + wallet = await api_users_create_user_wallet( + user.id, name="eur wallet", currency="EUR" + ) assert wallet.currency == "EUR" diff --git a/tests/api/test_wallet_api.py b/tests/api/test_wallet_api.py index 4087c67b2..3293f76d7 100644 --- a/tests/api/test_wallet_api.py +++ b/tests/api/test_wallet_api.py @@ -13,7 +13,9 @@ def _admin_headers(adminkey: str) -> dict[str, str]: @pytest.mark.anyio -async def test_wallet_api_share_invite_reject_accept_and_delete(http_client: AsyncClient): +async def test_wallet_api_share_invite_reject_accept_and_delete( + http_client: AsyncClient, +): owner = await create_user_account( Account( id=uuid4().hex, @@ -129,7 +131,9 @@ async def test_wallet_api_paginated_update_reset_and_store_paylinks( assert renamed.json()["name"] == "renamed-wallet" original_admin_key = extra_wallet.adminkey - reset = await http_client.put(f"/api/v1/wallet/reset/{extra_wallet.id}?usr={user.id}") + reset = await http_client.put( + f"/api/v1/wallet/reset/{extra_wallet.id}?usr={user.id}" + ) assert reset.status_code == 200 assert reset.json()["adminkey"] != original_admin_key @@ -181,4 +185,6 @@ async def test_wallet_api_shared_wallet_requires_source_id(http_client: AsyncCli json={"wallet_type": "lightning-shared"}, ) assert response.status_code == 400 - assert response.json()["detail"] == "Shared wallet ID is required for shared wallets." + assert ( + response.json()["detail"] == "Shared wallet ID is required for shared wallets." + )