chore: fix lint
This commit is contained in:
@@ -40,6 +40,7 @@ def get_random_string(iterations: int = 10):
|
|||||||
async def get_random_invoice_data():
|
async def get_random_invoice_data():
|
||||||
return {"out": False, "amount": 10, "memo": f"test_memo_{get_random_string(10)}"}
|
return {"out": False, "amount": 10, "memo": f"test_memo_{get_random_string(10)}"}
|
||||||
|
|
||||||
|
|
||||||
settings.lnbits_backend_wallet_class = "FakeWallet"
|
settings.lnbits_backend_wallet_class = "FakeWallet"
|
||||||
set_funding_source("FakeWallet")
|
set_funding_source("FakeWallet")
|
||||||
funding_source = get_funding_source()
|
funding_source = get_funding_source()
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ from lnbits.utils.exchange_rates import (
|
|||||||
|
|
||||||
|
|
||||||
class MockResponse:
|
class MockResponse:
|
||||||
def __init__(self, *, text: str = "", json_data=None, error: Exception | None = None):
|
def __init__(
|
||||||
|
self, *, text: str = "", json_data=None, error: Exception | None = None
|
||||||
|
):
|
||||||
self.text = text
|
self.text = text
|
||||||
self._json_data = json_data or {}
|
self._json_data = json_data or {}
|
||||||
self._error = error
|
self._error = error
|
||||||
@@ -263,7 +265,9 @@ async def test_btc_rates_skips_unsupported_and_failing_providers(
|
|||||||
)
|
)
|
||||||
client = MockAsyncClient(MockResponse(error=httpx.HTTPError("boom")))
|
client = MockAsyncClient(MockResponse(error=httpx.HTTPError("boom")))
|
||||||
mocker.patch.object(settings, "lnbits_allowed_currencies", [])
|
mocker.patch.object(settings, "lnbits_allowed_currencies", [])
|
||||||
mocker.patch.object(settings, "lnbits_exchange_rate_providers", [unsupported, failing])
|
mocker.patch.object(
|
||||||
|
settings, "lnbits_exchange_rate_providers", [unsupported, failing]
|
||||||
|
)
|
||||||
mocker.patch("lnbits.utils.exchange_rates.httpx.AsyncClient", return_value=client)
|
mocker.patch("lnbits.utils.exchange_rates.httpx.AsyncClient", return_value=client)
|
||||||
|
|
||||||
assert await btc_rates("usd") == []
|
assert await btc_rates("usd") == []
|
||||||
|
|||||||
@@ -17,9 +17,11 @@ from lnbits.core.services.fiat_providers import (
|
|||||||
check_fiat_status,
|
check_fiat_status,
|
||||||
check_stripe_signature,
|
check_stripe_signature,
|
||||||
handle_fiat_payment_confirmation,
|
handle_fiat_payment_confirmation,
|
||||||
test_connection as fiat_provider_connection,
|
|
||||||
verify_paypal_webhook,
|
verify_paypal_webhook,
|
||||||
)
|
)
|
||||||
|
from lnbits.core.services.fiat_providers import (
|
||||||
|
test_connection as fiat_provider_connection,
|
||||||
|
)
|
||||||
from lnbits.core.services.users import create_user_account
|
from lnbits.core.services.users import create_user_account
|
||||||
from lnbits.fiat.base import FiatInvoiceResponse, FiatPaymentStatus, FiatStatusResponse
|
from lnbits.fiat.base import FiatInvoiceResponse, FiatPaymentStatus, FiatStatusResponse
|
||||||
from lnbits.settings import Settings
|
from lnbits.settings import Settings
|
||||||
@@ -577,9 +579,7 @@ async def test_verify_paypal_webhook_requires_headers(settings: Settings):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
async def test_verify_paypal_webhook_success(
|
async def test_verify_paypal_webhook_success(settings: Settings, mocker: MockerFixture):
|
||||||
settings: Settings, mocker: MockerFixture
|
|
||||||
):
|
|
||||||
settings.paypal_webhook_id = "webhook-id"
|
settings.paypal_webhook_id = "webhook-id"
|
||||||
client = MockHTTPClient(
|
client = MockHTTPClient(
|
||||||
[
|
[
|
||||||
@@ -648,7 +648,9 @@ async def test_test_connection_reports_provider_status(mocker: MockerFixture):
|
|||||||
assert missing_status.message == "Fiat provider 'stripe' not found."
|
assert missing_status.message == "Fiat provider 'stripe' not found."
|
||||||
|
|
||||||
provider = mocker.Mock()
|
provider = mocker.Mock()
|
||||||
provider.status = AsyncMock(return_value=FiatStatusResponse(error_message="bad key"))
|
provider.status = AsyncMock(
|
||||||
|
return_value=FiatStatusResponse(error_message="bad key")
|
||||||
|
)
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
"lnbits.core.services.fiat_providers.get_fiat_provider",
|
"lnbits.core.services.fiat_providers.get_fiat_provider",
|
||||||
AsyncMock(return_value=provider),
|
AsyncMock(return_value=provider),
|
||||||
|
|||||||
@@ -264,7 +264,10 @@ def test_path_and_case_helpers():
|
|||||||
assert normalize_path("/upgrades/ext/assets/app.js") == "/assets/app.js"
|
assert normalize_path("/upgrades/ext/assets/app.js") == "/assets/app.js"
|
||||||
assert normalize_endpoint("example.com/") == "https://example.com"
|
assert normalize_endpoint("example.com/") == "https://example.com"
|
||||||
assert normalize_endpoint("ws://socket.example.com") == "ws://socket.example.com"
|
assert normalize_endpoint("ws://socket.example.com") == "ws://socket.example.com"
|
||||||
assert normalize_endpoint("http://example.com/", add_proto=False) == "http://example.com"
|
assert (
|
||||||
|
normalize_endpoint("http://example.com/", add_proto=False)
|
||||||
|
== "http://example.com"
|
||||||
|
)
|
||||||
|
|
||||||
assert camel_to_words("CamelCaseName") == "Camel Case Name"
|
assert camel_to_words("CamelCaseName") == "Camel Case Name"
|
||||||
assert camel_to_snake("CamelCaseName") == "camel_case_name"
|
assert camel_to_snake("CamelCaseName") == "camel_case_name"
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import json
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from lnbits.db import (
|
from lnbits.db import (
|
||||||
dict_to_submodel,
|
|
||||||
dict_to_model,
|
dict_to_model,
|
||||||
|
dict_to_submodel,
|
||||||
insert_query,
|
insert_query,
|
||||||
model_to_dict,
|
model_to_dict,
|
||||||
update_query,
|
update_query,
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from types import SimpleNamespace
|
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from fastapi import UploadFile
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from pytest_mock.plugin import MockerFixture
|
from pytest_mock.plugin import MockerFixture
|
||||||
|
from starlette.datastructures import Headers
|
||||||
|
|
||||||
from lnbits.core.crud import create_account
|
from lnbits.core.crud import create_account
|
||||||
from lnbits.core.crud.assets import get_user_asset, get_user_assets_count
|
from lnbits.core.crud.assets import get_user_asset, get_user_assets_count
|
||||||
@@ -19,24 +20,34 @@ async def _create_user() -> str:
|
|||||||
return user_id
|
return user_id
|
||||||
|
|
||||||
|
|
||||||
|
def _make_upload_file(
|
||||||
|
contents: bytes,
|
||||||
|
*,
|
||||||
|
filename: str,
|
||||||
|
content_type: str | None,
|
||||||
|
) -> UploadFile:
|
||||||
|
headers = (
|
||||||
|
Headers({"content-type": content_type}) if content_type is not None else None
|
||||||
|
)
|
||||||
|
return UploadFile(BytesIO(contents), filename=filename, headers=headers)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
async def test_create_user_asset_validates_upload_constraints(
|
async def test_create_user_asset_validates_upload_constraints(
|
||||||
settings: Settings, mocker: MockerFixture
|
settings: Settings, mocker: MockerFixture
|
||||||
):
|
):
|
||||||
file_without_type = SimpleNamespace(
|
file_without_type = _make_upload_file(b"hello", filename="a.txt", content_type=None)
|
||||||
content_type=None,
|
|
||||||
filename="a.txt",
|
|
||||||
read=mocker.AsyncMock(return_value=b"hello"),
|
|
||||||
)
|
|
||||||
with pytest.raises(ValueError, match="File must have a content type."):
|
with pytest.raises(ValueError, match="File must have a content type."):
|
||||||
await create_user_asset("user-1", file_without_type, is_public=False)
|
await create_user_asset("user-1", file_without_type, is_public=False)
|
||||||
|
|
||||||
bad_type = SimpleNamespace(
|
bad_type = _make_upload_file(
|
||||||
content_type="application/x-msdownload",
|
b"hello",
|
||||||
filename="bad.bin",
|
filename="bad.bin",
|
||||||
read=mocker.AsyncMock(return_value=b"hello"),
|
content_type="application/x-msdownload",
|
||||||
)
|
)
|
||||||
with pytest.raises(ValueError, match="File type 'application/x-msdownload' not allowed."):
|
with pytest.raises(
|
||||||
|
ValueError, match="File type 'application/x-msdownload' not allowed."
|
||||||
|
):
|
||||||
await create_user_asset("user-1", bad_type, is_public=False)
|
await create_user_asset("user-1", bad_type, is_public=False)
|
||||||
|
|
||||||
original_max_assets = settings.lnbits_max_assets_per_user
|
original_max_assets = settings.lnbits_max_assets_per_user
|
||||||
@@ -47,27 +58,25 @@ async def test_create_user_asset_validates_upload_constraints(
|
|||||||
settings.lnbits_max_asset_size_mb = 1
|
settings.lnbits_max_asset_size_mb = 1
|
||||||
settings.lnbits_assets_no_limit_users = []
|
settings.lnbits_assets_no_limit_users = []
|
||||||
limited_user = await _create_user()
|
limited_user = await _create_user()
|
||||||
allowed_type = SimpleNamespace(
|
allowed_type = _make_upload_file(
|
||||||
content_type="text/plain",
|
b"hello", filename="ok.txt", content_type="text/plain"
|
||||||
filename="ok.txt",
|
|
||||||
read=mocker.AsyncMock(return_value=b"hello"),
|
|
||||||
)
|
)
|
||||||
await create_user_asset(limited_user, allowed_type, is_public=False)
|
await create_user_asset(limited_user, allowed_type, is_public=False)
|
||||||
|
|
||||||
blocked_by_count = SimpleNamespace(
|
blocked_by_count = _make_upload_file(
|
||||||
content_type="text/plain",
|
b"again",
|
||||||
filename="again.txt",
|
filename="again.txt",
|
||||||
read=mocker.AsyncMock(return_value=b"again"),
|
content_type="text/plain",
|
||||||
)
|
)
|
||||||
with pytest.raises(ValueError, match="Max upload count of 1 exceeded."):
|
with pytest.raises(ValueError, match="Max upload count of 1 exceeded."):
|
||||||
await create_user_asset(limited_user, blocked_by_count, is_public=False)
|
await create_user_asset(limited_user, blocked_by_count, is_public=False)
|
||||||
|
|
||||||
settings.lnbits_max_asset_size_mb = 0.000001
|
settings.lnbits_max_asset_size_mb = 0.000001
|
||||||
oversized_user = await _create_user()
|
oversized_user = await _create_user()
|
||||||
large_file = SimpleNamespace(
|
large_file = _make_upload_file(
|
||||||
content_type="text/plain",
|
b"0123456789",
|
||||||
filename="ok.txt",
|
filename="ok.txt",
|
||||||
read=mocker.AsyncMock(return_value=b"0123456789"),
|
content_type="text/plain",
|
||||||
)
|
)
|
||||||
with pytest.raises(ValueError, match="File limit of 1e-06MB exceeded."):
|
with pytest.raises(ValueError, match="File limit of 1e-06MB exceeded."):
|
||||||
await create_user_asset(oversized_user, large_file, is_public=False)
|
await create_user_asset(oversized_user, large_file, is_public=False)
|
||||||
@@ -84,11 +93,7 @@ async def test_create_user_asset_success(mocker: MockerFixture):
|
|||||||
"lnbits.core.services.assets.thumbnail_from_bytes",
|
"lnbits.core.services.assets.thumbnail_from_bytes",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
)
|
)
|
||||||
file = SimpleNamespace(
|
file = _make_upload_file(b"hello", filename="hello.txt", content_type="text/plain")
|
||||||
content_type="text/plain",
|
|
||||||
filename="hello.txt",
|
|
||||||
read=mocker.AsyncMock(return_value=b"hello"),
|
|
||||||
)
|
|
||||||
|
|
||||||
asset = await create_user_asset(user_id, file, is_public=True)
|
asset = await create_user_asset(user_id, file, is_public=True)
|
||||||
stored = await get_user_asset(user_id, asset.id)
|
stored = await get_user_asset(user_id, asset.id)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
from uuid import uuid4
|
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from pytest_mock.plugin import MockerFixture
|
from pytest_mock.plugin import MockerFixture
|
||||||
@@ -8,7 +8,6 @@ from lnbits.core.crud import (
|
|||||||
create_installed_extension,
|
create_installed_extension,
|
||||||
delete_installed_extension,
|
delete_installed_extension,
|
||||||
get_installed_extension,
|
get_installed_extension,
|
||||||
get_installed_extensions,
|
|
||||||
)
|
)
|
||||||
from lnbits.core.models.extensions import (
|
from lnbits.core.models.extensions import (
|
||||||
Extension,
|
Extension,
|
||||||
@@ -57,7 +56,9 @@ def _installable_extension(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
async def test_install_extension_rejects_incompatible_release(tmp_path, settings: Settings):
|
async def test_install_extension_rejects_incompatible_release(
|
||||||
|
tmp_path, settings: Settings
|
||||||
|
):
|
||||||
ext_info = _installable_extension(f"ext_{uuid4().hex[:8]}", compatible=False)
|
ext_info = _installable_extension(f"ext_{uuid4().hex[:8]}", compatible=False)
|
||||||
original_data_folder = settings.lnbits_data_folder
|
original_data_folder = settings.lnbits_data_folder
|
||||||
original_extensions_path = settings.lnbits_extensions_path
|
original_extensions_path = settings.lnbits_extensions_path
|
||||||
@@ -293,7 +294,9 @@ async def test_get_valid_extensions_and_single_extension_respect_settings(
|
|||||||
assert ext_id_one in valid_codes
|
assert ext_id_one in valid_codes
|
||||||
assert ext_id_two not in valid_codes
|
assert ext_id_two not in valid_codes
|
||||||
|
|
||||||
assert await get_valid_extension(ext_id_one, include_deactivated=True) is not None
|
assert (
|
||||||
|
await get_valid_extension(ext_id_one, include_deactivated=True) is not None
|
||||||
|
)
|
||||||
|
|
||||||
settings.lnbits_extensions_deactivate_all = True
|
settings.lnbits_extensions_deactivate_all = True
|
||||||
assert await get_valid_extensions(include_deactivated=False) == []
|
assert await get_valid_extensions(include_deactivated=False) == []
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
from typing import Any, cast
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
@@ -139,10 +140,11 @@ async def test_check_server_balance_against_node_notifies_and_switches(
|
|||||||
async def test_check_balance_delta_changed_tracks_and_notifies(
|
async def test_check_balance_delta_changed_tracks_and_notifies(
|
||||||
settings: Settings, mocker: MockerFixture
|
settings: Settings, mocker: MockerFixture
|
||||||
):
|
):
|
||||||
|
settings_any = cast(Any, settings)
|
||||||
original_latest = settings.latest_balance_delta_sats
|
original_latest = settings.latest_balance_delta_sats
|
||||||
original_threshold = settings.notification_balance_delta_threshold_sats
|
original_threshold = settings.notification_balance_delta_threshold_sats
|
||||||
try:
|
try:
|
||||||
settings.latest_balance_delta_sats = None
|
settings_any.latest_balance_delta_sats = None
|
||||||
settings.notification_balance_delta_threshold_sats = 3
|
settings.notification_balance_delta_threshold_sats = 3
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
"lnbits.core.services.funding_source.get_balance_delta",
|
"lnbits.core.services.funding_source.get_balance_delta",
|
||||||
@@ -165,5 +167,5 @@ async def test_check_balance_delta_changed_tracks_and_notifies(
|
|||||||
enqueue.assert_called_once()
|
enqueue.assert_called_once()
|
||||||
assert settings.latest_balance_delta_sats == 10
|
assert settings.latest_balance_delta_sats == 10
|
||||||
finally:
|
finally:
|
||||||
settings.latest_balance_delta_sats = original_latest
|
settings_any.latest_balance_delta_sats = original_latest
|
||||||
settings.notification_balance_delta_threshold_sats = original_threshold
|
settings.notification_balance_delta_threshold_sats = original_threshold
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
from bolt11.types import MilliSatoshi
|
||||||
from lnurl import (
|
from lnurl import (
|
||||||
LnAddress,
|
LnAddress,
|
||||||
LnurlErrorResponse,
|
LnurlErrorResponse,
|
||||||
@@ -10,6 +11,8 @@ from lnurl import (
|
|||||||
LnurlSuccessResponse,
|
LnurlSuccessResponse,
|
||||||
LnurlWithdrawResponse,
|
LnurlWithdrawResponse,
|
||||||
)
|
)
|
||||||
|
from lnurl.types import CallbackUrl, LightningInvoice, LnurlPayMetadata
|
||||||
|
from pydantic import parse_obj_as
|
||||||
from pytest_mock.plugin import MockerFixture
|
from pytest_mock.plugin import MockerFixture
|
||||||
|
|
||||||
from lnbits.core.crud import create_account, create_wallet, get_wallet
|
from lnbits.core.crud import create_account, create_wallet, get_wallet
|
||||||
@@ -34,10 +37,12 @@ TEST_BOLT11 = (
|
|||||||
|
|
||||||
def _make_pay_response() -> LnurlPayResponse:
|
def _make_pay_response() -> LnurlPayResponse:
|
||||||
return LnurlPayResponse(
|
return LnurlPayResponse(
|
||||||
callback="https://example.com/callback",
|
callback=parse_obj_as(CallbackUrl, "https://example.com/callback"),
|
||||||
minSendable=1,
|
minSendable=MilliSatoshi(1),
|
||||||
maxSendable=10_000,
|
maxSendable=MilliSatoshi(10_000),
|
||||||
metadata='[["text/plain","Test"],["text/identifier","alice@example.com"]]',
|
metadata=LnurlPayMetadata(
|
||||||
|
'[["text/plain","Test"],["text/identifier","alice@example.com"]]'
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -60,10 +65,10 @@ async def _create_wallet() -> Wallet:
|
|||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
async def test_perform_withdraw_success_and_validation(mocker: MockerFixture):
|
async def test_perform_withdraw_success_and_validation(mocker: MockerFixture):
|
||||||
withdraw_response = LnurlWithdrawResponse(
|
withdraw_response = LnurlWithdrawResponse(
|
||||||
callback="https://example.com/callback",
|
callback=parse_obj_as(CallbackUrl, "https://example.com/callback"),
|
||||||
k1="k1",
|
k1="k1",
|
||||||
minWithdrawable=1,
|
minWithdrawable=MilliSatoshi(1),
|
||||||
maxWithdrawable=1000,
|
maxWithdrawable=MilliSatoshi(1000),
|
||||||
defaultDescription="test",
|
defaultDescription="test",
|
||||||
)
|
)
|
||||||
execute_withdraw_mock = mocker.patch(
|
execute_withdraw_mock = mocker.patch(
|
||||||
@@ -107,7 +112,9 @@ async def test_get_pr_from_lnurl_success_and_error(mocker: MockerFixture):
|
|||||||
)
|
)
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
"lnbits.core.services.lnurl.execute_pay_request",
|
"lnbits.core.services.lnurl.execute_pay_request",
|
||||||
mocker.AsyncMock(return_value=LnurlPayActionResponse(pr=TEST_BOLT11)),
|
mocker.AsyncMock(
|
||||||
|
return_value=LnurlPayActionResponse(pr=LightningInvoice(TEST_BOLT11))
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert await get_pr_from_lnurl("lnurl", 1000, comment="hello") == TEST_BOLT11
|
assert await get_pr_from_lnurl("lnurl", 1000, comment="hello") == TEST_BOLT11
|
||||||
@@ -125,7 +132,9 @@ async def test_fetch_lnurl_pay_request_converts_currency_and_stores_paylink(
|
|||||||
mocker: MockerFixture,
|
mocker: MockerFixture,
|
||||||
):
|
):
|
||||||
pay_response = _make_pay_response()
|
pay_response = _make_pay_response()
|
||||||
action_response = LnurlPayActionResponse(pr=TEST_BOLT11, disposable=False)
|
action_response = LnurlPayActionResponse(
|
||||||
|
pr=LightningInvoice(TEST_BOLT11), disposable=False
|
||||||
|
)
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
"lnbits.core.services.lnurl.fiat_amount_as_satoshis",
|
"lnbits.core.services.lnurl.fiat_amount_as_satoshis",
|
||||||
mocker.AsyncMock(return_value=100),
|
mocker.AsyncMock(return_value=100),
|
||||||
@@ -146,8 +155,11 @@ async def test_fetch_lnurl_pay_request_converts_currency_and_stores_paylink(
|
|||||||
assert response == pay_response
|
assert response == pay_response
|
||||||
assert action == action_response
|
assert action == action_response
|
||||||
execute_mock.assert_awaited_once()
|
execute_mock.assert_awaited_once()
|
||||||
|
assert execute_mock.await_args is not None
|
||||||
assert execute_mock.await_args.kwargs["msat"] == 100_000
|
assert execute_mock.await_args.kwargs["msat"] == 100_000
|
||||||
store_paylink_mock.assert_awaited_once_with(pay_response, action_response, wallet, None)
|
store_paylink_mock.assert_awaited_once_with(
|
||||||
|
pay_response, action_response, wallet, None
|
||||||
|
)
|
||||||
|
|
||||||
with pytest.raises(LnurlResponseException, match="No LNURL pay request provided."):
|
with pytest.raises(LnurlResponseException, match="No LNURL pay request provided."):
|
||||||
await fetch_lnurl_pay_request(CreateLnurlPayment(amount=1))
|
await fetch_lnurl_pay_request(CreateLnurlPayment(amount=1))
|
||||||
@@ -157,9 +169,13 @@ async def test_fetch_lnurl_pay_request_converts_currency_and_stores_paylink(
|
|||||||
async def test_store_paylink_appends_and_updates_existing():
|
async def test_store_paylink_appends_and_updates_existing():
|
||||||
wallet = await _create_wallet()
|
wallet = await _create_wallet()
|
||||||
pay_response = _make_pay_response()
|
pay_response = _make_pay_response()
|
||||||
action_response = LnurlPayActionResponse(pr=TEST_BOLT11, disposable=False)
|
action_response = LnurlPayActionResponse(
|
||||||
|
pr=LightningInvoice(TEST_BOLT11), disposable=False
|
||||||
|
)
|
||||||
|
|
||||||
await store_paylink(pay_response, action_response, wallet, LnAddress("alice@example.com"))
|
await store_paylink(
|
||||||
|
pay_response, action_response, wallet, LnAddress("alice@example.com")
|
||||||
|
)
|
||||||
stored_wallet = await get_wallet(wallet.id)
|
stored_wallet = await get_wallet(wallet.id)
|
||||||
|
|
||||||
assert stored_wallet is not None
|
assert stored_wallet is not None
|
||||||
@@ -167,7 +183,9 @@ async def test_store_paylink_appends_and_updates_existing():
|
|||||||
assert stored_wallet.stored_paylinks.links[0].lnurl == "alice@example.com"
|
assert stored_wallet.stored_paylinks.links[0].lnurl == "alice@example.com"
|
||||||
|
|
||||||
first_used = stored_wallet.stored_paylinks.links[0].last_used
|
first_used = stored_wallet.stored_paylinks.links[0].last_used
|
||||||
await store_paylink(pay_response, action_response, wallet, LnAddress("alice@example.com"))
|
await store_paylink(
|
||||||
|
pay_response, action_response, wallet, LnAddress("alice@example.com")
|
||||||
|
)
|
||||||
stored_wallet = await get_wallet(wallet.id)
|
stored_wallet = await get_wallet(wallet.id)
|
||||||
|
|
||||||
assert stored_wallet is not None
|
assert stored_wallet is not None
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
from types import SimpleNamespace
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
import pytest
|
import pytest
|
||||||
from pytest_mock.plugin import MockerFixture
|
from pytest_mock.plugin import MockerFixture
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from datetime import datetime, timezone
|
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
@@ -7,8 +6,8 @@ from uuid import uuid4
|
|||||||
|
|
||||||
import httpx
|
import httpx
|
||||||
import pytest
|
import pytest
|
||||||
from pywebpush import WebPushException
|
|
||||||
from pytest_mock.plugin import MockerFixture
|
from pytest_mock.plugin import MockerFixture
|
||||||
|
from pywebpush import WebPushException
|
||||||
|
|
||||||
from lnbits.core.crud import (
|
from lnbits.core.crud import (
|
||||||
create_account,
|
create_account,
|
||||||
@@ -37,9 +36,9 @@ from lnbits.core.services.notifications import (
|
|||||||
send_chat_payment_notification,
|
send_chat_payment_notification,
|
||||||
send_email,
|
send_email,
|
||||||
send_email_notification,
|
send_email_notification,
|
||||||
send_notification,
|
|
||||||
send_nostr_notification,
|
send_nostr_notification,
|
||||||
send_nostr_notifications,
|
send_nostr_notifications,
|
||||||
|
send_notification,
|
||||||
send_payment_notification,
|
send_payment_notification,
|
||||||
send_payment_push_notification,
|
send_payment_push_notification,
|
||||||
send_push_notification,
|
send_push_notification,
|
||||||
@@ -118,7 +117,9 @@ async def _create_payment(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.anyio
|
@pytest.mark.anyio
|
||||||
async def test_enqueue_and_process_notifications(settings: Settings, mocker: MockerFixture):
|
async def test_enqueue_and_process_notifications(
|
||||||
|
settings: Settings, mocker: MockerFixture
|
||||||
|
):
|
||||||
queue: asyncio.Queue = asyncio.Queue()
|
queue: asyncio.Queue = asyncio.Queue()
|
||||||
admin_mock = mocker.patch(
|
admin_mock = mocker.patch(
|
||||||
"lnbits.core.services.notifications.send_admin_notification",
|
"lnbits.core.services.notifications.send_admin_notification",
|
||||||
@@ -138,6 +139,7 @@ async def test_enqueue_and_process_notifications(settings: Settings, mocker: Moc
|
|||||||
await process_next_notification()
|
await process_next_notification()
|
||||||
|
|
||||||
assert admin_mock.await_count == 1
|
assert admin_mock.await_count == 1
|
||||||
|
assert admin_mock.await_args is not None
|
||||||
assert admin_mock.await_args.args[0].startswith(f"[{settings.lnbits_site_title}]")
|
assert admin_mock.await_args.args[0].startswith(f"[{settings.lnbits_site_title}]")
|
||||||
assert "alice" in admin_mock.await_args.args[0]
|
assert "alice" in admin_mock.await_args.args[0]
|
||||||
assert admin_mock.await_args.args[1] == NotificationType.settings_update.value
|
assert admin_mock.await_args.args[1] == NotificationType.settings_update.value
|
||||||
@@ -151,6 +153,7 @@ async def test_enqueue_and_process_notifications(settings: Settings, mocker: Moc
|
|||||||
await process_next_notification()
|
await process_next_notification()
|
||||||
|
|
||||||
assert user_mock.await_count == 1
|
assert user_mock.await_count == 1
|
||||||
|
assert user_mock.await_args is not None
|
||||||
assert user_mock.await_args.args[0] == user_notifications
|
assert user_mock.await_args.args[0] == user_notifications
|
||||||
assert "hello" in user_mock.await_args.args[1]
|
assert "hello" in user_mock.await_args.args[1]
|
||||||
assert user_mock.await_args.args[2] == NotificationType.text_message.value
|
assert user_mock.await_args.args[2] == NotificationType.text_message.value
|
||||||
@@ -405,6 +408,7 @@ async def test_dispatch_webhook_marks_missing_invalid_and_failed_requests(
|
|||||||
assert (await get_payment(payment.checking_id)).webhook_status == "-1"
|
assert (await get_payment(payment.checking_id)).webhook_status == "-1"
|
||||||
|
|
||||||
invalid_payment = await _create_payment(wallet, webhook="https://invalid.example")
|
invalid_payment = await _create_payment(wallet, webhook="https://invalid.example")
|
||||||
|
assert invalid_payment.webhook is not None
|
||||||
invalid_client = MockHTTPClient(
|
invalid_client = MockHTTPClient(
|
||||||
post_response=httpx.Response(
|
post_response=httpx.Response(
|
||||||
200,
|
200,
|
||||||
@@ -422,9 +426,13 @@ async def test_dispatch_webhook_marks_missing_invalid_and_failed_requests(
|
|||||||
)
|
)
|
||||||
|
|
||||||
await dispatch_webhook(invalid_payment)
|
await dispatch_webhook(invalid_payment)
|
||||||
assert (await get_payment(invalid_payment.checking_id)).webhook_status in {"-1", "200"}
|
assert (await get_payment(invalid_payment.checking_id)).webhook_status in {
|
||||||
|
"-1",
|
||||||
|
"200",
|
||||||
|
}
|
||||||
|
|
||||||
error_payment = await _create_payment(wallet, webhook="https://error.example")
|
error_payment = await _create_payment(wallet, webhook="https://error.example")
|
||||||
|
assert error_payment.webhook is not None
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
"lnbits.core.services.notifications.check_callback_url",
|
"lnbits.core.services.notifications.check_callback_url",
|
||||||
return_value=None,
|
return_value=None,
|
||||||
@@ -443,6 +451,7 @@ async def test_dispatch_webhook_marks_missing_invalid_and_failed_requests(
|
|||||||
assert (await get_payment(error_payment.checking_id)).webhook_status == "500"
|
assert (await get_payment(error_payment.checking_id)).webhook_status == "500"
|
||||||
|
|
||||||
request_payment = await _create_payment(wallet, webhook="https://request.example")
|
request_payment = await _create_payment(wallet, webhook="https://request.example")
|
||||||
|
assert request_payment.webhook is not None
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
"lnbits.core.services.notifications.httpx.AsyncClient",
|
"lnbits.core.services.notifications.httpx.AsyncClient",
|
||||||
return_value=MockHTTPClient(
|
return_value=MockHTTPClient(
|
||||||
@@ -574,6 +583,7 @@ async def test_send_payment_push_notification_and_cleanup_gone_subscriptions(
|
|||||||
|
|
||||||
await send_payment_push_notification(wallet, payment)
|
await send_payment_push_notification(wallet, payment)
|
||||||
|
|
||||||
|
assert send_push_mock.await_args is not None
|
||||||
assert send_push_mock.await_args.args[0].endpoint == subscription.endpoint
|
assert send_push_mock.await_args.args[0].endpoint == subscription.endpoint
|
||||||
assert send_push_mock.await_args.args[1] == f"LNbits: {wallet.name}"
|
assert send_push_mock.await_args.args[1] == f"LNbits: {wallet.name}"
|
||||||
assert "received 2 sats" in send_push_mock.await_args.args[2]
|
assert "received 2 sats" in send_push_mock.await_args.args[2]
|
||||||
|
|||||||
@@ -17,13 +17,12 @@ from lnbits.core.models import (
|
|||||||
Account,
|
Account,
|
||||||
CreateInvoice,
|
CreateInvoice,
|
||||||
CreatePayment,
|
CreatePayment,
|
||||||
PaymentDailyStats,
|
|
||||||
PaymentState,
|
PaymentState,
|
||||||
Wallet,
|
Wallet,
|
||||||
)
|
)
|
||||||
from lnbits.core.services.payments import (
|
from lnbits.core.services.payments import (
|
||||||
cancel_hold_invoice,
|
|
||||||
calculate_fiat_amounts,
|
calculate_fiat_amounts,
|
||||||
|
cancel_hold_invoice,
|
||||||
check_payment_status,
|
check_payment_status,
|
||||||
check_pending_payments,
|
check_pending_payments,
|
||||||
check_time_limit_between_transactions,
|
check_time_limit_between_transactions,
|
||||||
@@ -55,7 +54,9 @@ def _account() -> Account:
|
|||||||
async def _create_wallet() -> Wallet:
|
async def _create_wallet() -> Wallet:
|
||||||
account = _account()
|
account = _account()
|
||||||
await create_account(account)
|
await create_account(account)
|
||||||
return await create_wallet(user_id=account.id, wallet_name=f"wallet_{account.id[:8]}")
|
return await create_wallet(
|
||||||
|
user_id=account.id, wallet_name=f"wallet_{account.id[:8]}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def _create_payment(
|
async def _create_payment(
|
||||||
@@ -103,7 +104,10 @@ async def test_create_payment_request_routes_by_invoice_type(mocker: MockerFixtu
|
|||||||
mocker.AsyncMock(return_value=fiat_payment),
|
mocker.AsyncMock(return_value=fiat_payment),
|
||||||
)
|
)
|
||||||
|
|
||||||
assert await create_payment_request("wallet-1", CreateInvoice(amount=1)) == wallet_payment
|
assert (
|
||||||
|
await create_payment_request("wallet-1", CreateInvoice(amount=1))
|
||||||
|
== wallet_payment
|
||||||
|
)
|
||||||
assert (
|
assert (
|
||||||
await create_payment_request(
|
await create_payment_request(
|
||||||
"wallet-1",
|
"wallet-1",
|
||||||
@@ -165,7 +169,9 @@ async def test_check_pending_payments_skips_voidwallet_and_updates_recent_items(
|
|||||||
"lnbits.core.services.payments.get_funding_source",
|
"lnbits.core.services.payments.get_funding_source",
|
||||||
return_value=VoidWallet(),
|
return_value=VoidWallet(),
|
||||||
)
|
)
|
||||||
sleep_mock = mocker.patch("lnbits.core.services.payments.asyncio.sleep", mocker.AsyncMock())
|
sleep_mock = mocker.patch(
|
||||||
|
"lnbits.core.services.payments.asyncio.sleep", mocker.AsyncMock()
|
||||||
|
)
|
||||||
|
|
||||||
await check_pending_payments()
|
await check_pending_payments()
|
||||||
sleep_mock.assert_not_awaited()
|
sleep_mock.assert_not_awaited()
|
||||||
|
|||||||
@@ -2,7 +2,11 @@ import pytest
|
|||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
from pytest_mock.plugin import MockerFixture
|
from pytest_mock.plugin import MockerFixture
|
||||||
|
|
||||||
from lnbits.core.crud import create_admin_settings, delete_admin_settings, get_super_settings
|
from lnbits.core.crud import (
|
||||||
|
create_admin_settings,
|
||||||
|
delete_admin_settings,
|
||||||
|
get_super_settings,
|
||||||
|
)
|
||||||
from lnbits.core.crud.settings import get_settings_field
|
from lnbits.core.crud.settings import get_settings_field
|
||||||
from lnbits.core.services.settings import (
|
from lnbits.core.services.settings import (
|
||||||
check_webpush_settings,
|
check_webpush_settings,
|
||||||
@@ -84,7 +88,9 @@ async def test_check_webpush_settings_generates_and_persists_keys(
|
|||||||
settings.lnbits_webpush_pubkey = None
|
settings.lnbits_webpush_pubkey = None
|
||||||
settings.lnbits_admin_ui = True
|
settings.lnbits_admin_ui = True
|
||||||
mocker.patch("lnbits.core.services.settings.Vapid", return_value=FakeVapid())
|
mocker.patch("lnbits.core.services.settings.Vapid", return_value=FakeVapid())
|
||||||
mocker.patch("lnbits.core.services.settings.b64urlencode", return_value="public-key")
|
mocker.patch(
|
||||||
|
"lnbits.core.services.settings.b64urlencode", return_value="public-key"
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
await check_webpush_settings()
|
await check_webpush_settings()
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,8 @@ from lnbits.core.crud import (
|
|||||||
from lnbits.core.crud.settings import get_settings_field, set_settings_field
|
from lnbits.core.crud.settings import get_settings_field, set_settings_field
|
||||||
from lnbits.core.models import Account
|
from lnbits.core.models import Account
|
||||||
from lnbits.core.models.extensions import UserExtension
|
from lnbits.core.models.extensions import UserExtension
|
||||||
from lnbits.core.models.users import RegisterUser, UserExtra
|
from lnbits.core.models.users import RegisterUser
|
||||||
|
from lnbits.core.services.settings import update_cached_settings
|
||||||
from lnbits.core.services.users import (
|
from lnbits.core.services.users import (
|
||||||
check_admin_settings,
|
check_admin_settings,
|
||||||
check_register_activation_settings,
|
check_register_activation_settings,
|
||||||
@@ -25,7 +26,6 @@ from lnbits.core.services.users import (
|
|||||||
update_user_account,
|
update_user_account,
|
||||||
update_user_extensions,
|
update_user_extensions,
|
||||||
)
|
)
|
||||||
from lnbits.core.services.settings import update_cached_settings
|
|
||||||
from lnbits.settings import Settings
|
from lnbits.settings import Settings
|
||||||
|
|
||||||
|
|
||||||
@@ -219,7 +219,9 @@ async def test_update_user_extensions_toggles_existing_and_creates_missing(
|
|||||||
finally:
|
finally:
|
||||||
settings.lnbits_user_default_extensions = original_default_exts
|
settings.lnbits_user_default_extensions = original_default_exts
|
||||||
|
|
||||||
await create_user_extension(UserExtension(user=user.id, extension="keep", active=True))
|
await create_user_extension(
|
||||||
|
UserExtension(user=user.id, extension="keep", active=True)
|
||||||
|
)
|
||||||
await create_user_extension(
|
await create_user_extension(
|
||||||
UserExtension(user=user.id, extension="enable", active=False)
|
UserExtension(user=user.id, extension="enable", active=False)
|
||||||
)
|
)
|
||||||
@@ -360,7 +362,9 @@ async def test_check_register_activation_settings_handles_invitation_codes(
|
|||||||
is None
|
is None
|
||||||
)
|
)
|
||||||
assert one_time not in settings.lnbits_register_one_time_activation_codes
|
assert one_time not in settings.lnbits_register_one_time_activation_codes
|
||||||
stored_codes = await get_settings_field("lnbits_register_one_time_activation_codes")
|
stored_codes = await get_settings_field(
|
||||||
|
"lnbits_register_one_time_activation_codes"
|
||||||
|
)
|
||||||
assert stored_codes is not None
|
assert stored_codes is not None
|
||||||
assert stored_codes.value == []
|
assert stored_codes.value == []
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import asyncio
|
from typing import cast
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from fastapi import WebSocketDisconnect
|
from fastapi import WebSocket, WebSocketDisconnect
|
||||||
from pytest_mock.plugin import MockerFixture
|
from pytest_mock.plugin import MockerFixture
|
||||||
|
|
||||||
from lnbits.core.services.websockets import (
|
from lnbits.core.services.websockets import (
|
||||||
@@ -37,7 +37,7 @@ async def test_websocket_connection_manager_connect_and_send():
|
|||||||
manager = WebsocketConnectionManager()
|
manager = WebsocketConnectionManager()
|
||||||
websocket = FakeWebSocket()
|
websocket = FakeWebSocket()
|
||||||
|
|
||||||
conn = await manager.connect("item-1", websocket)
|
conn = await manager.connect("item-1", cast(WebSocket, websocket))
|
||||||
await manager.send("item-1", "payload")
|
await manager.send("item-1", "payload")
|
||||||
|
|
||||||
assert websocket.accepted is True
|
assert websocket.accepted is True
|
||||||
@@ -52,7 +52,7 @@ async def test_websocket_connection_manager_listen_queues_messages_and_disconnec
|
|||||||
):
|
):
|
||||||
manager = WebsocketConnectionManager()
|
manager = WebsocketConnectionManager()
|
||||||
websocket = FakeWebSocket(["hello", WebSocketDisconnect()])
|
websocket = FakeWebSocket(["hello", WebSocketDisconnect()])
|
||||||
conn = await manager.connect("item-2", websocket)
|
conn = await manager.connect("item-2", cast(WebSocket, websocket))
|
||||||
original_running = settings.lnbits_running
|
original_running = settings.lnbits_running
|
||||||
try:
|
try:
|
||||||
settings.lnbits_running = True
|
settings.lnbits_running = True
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ def test_public_settings_from_settings(settings: Settings):
|
|||||||
def test_set_cli_settings_updates_runtime_settings(settings: Settings):
|
def test_set_cli_settings_updates_runtime_settings(settings: Settings):
|
||||||
original_host = settings.host
|
original_host = settings.host
|
||||||
try:
|
try:
|
||||||
set_cli_settings(host="0.0.0.0")
|
set_cli_settings(host="0.0.0.0") # noqa S104
|
||||||
assert settings.host == "0.0.0.0"
|
assert settings.host == "0.0.0.0" # noqa S104
|
||||||
finally:
|
finally:
|
||||||
settings.host = original_host
|
settings.host = original_host
|
||||||
|
|||||||
Reference in New Issue
Block a user