diff --git a/lnbits/core/services.py b/lnbits/core/services.py index 7afb5ec8a..89cddcad2 100644 --- a/lnbits/core/services.py +++ b/lnbits/core/services.py @@ -839,9 +839,6 @@ async def create_user_account( account: Optional[Account] = None, wallet_name: Optional[str] = None ) -> User: if not settings.new_accounts_allowed: - print("### self.lnbits_allow_new_accounts", settings.lnbits_allow_new_accounts) - print("### self.lnbits_allowed_users", settings.lnbits_allowed_users) - raise ValueError("Account creation is disabled.") if account: if account.username and await get_account_by_username(account.username): diff --git a/tests/api/test_admin_api.py b/tests/api/test_admin_api.py index 3220bf8f8..69df9161d 100644 --- a/tests/api/test_admin_api.py +++ b/tests/api/test_admin_api.py @@ -1,6 +1,7 @@ import pytest -from lnbits.settings import settings +from lnbits.core.models import User +from lnbits.settings import Settings @pytest.mark.asyncio @@ -18,7 +19,7 @@ async def test_admin_get_settings(client, superuser): @pytest.mark.asyncio -async def test_admin_update_settings(client, superuser): +async def test_admin_update_settings(client, superuser: User, settings: Settings): new_site_title = "UPDATED SITETITLE" response = await client.put( f"/admin/api/v1/settings?usr={superuser.id}", diff --git a/tests/api/test_api.py b/tests/api/test_api.py index 56fe36775..491d5f26f 100644 --- a/tests/api/test_api.py +++ b/tests/api/test_api.py @@ -18,7 +18,10 @@ from ..helpers import ( async def test_create_account(client, settings: Settings): settings.lnbits_allow_new_accounts = False response = await client.post("/api/v1/account", json={"name": "test"}) - assert response.status_code == 403 + + assert response.status_code == 400 + assert response.json().get("detail") == "Account creation is disabled." + settings.lnbits_allow_new_accounts = True response = await client.post("/api/v1/account", json={"name": "test"}) assert response.status_code == 200 diff --git a/tests/api/test_auth.py b/tests/api/test_auth.py index 9fcbc8cf4..efa27a2f0 100644 --- a/tests/api/test_auth.py +++ b/tests/api/test_auth.py @@ -62,17 +62,17 @@ async def test_login_alan_usr(user_alan: User, http_client: AsyncClient): @pytest.mark.asyncio async def test_login_usr_not_allowed( - user_alan: User, http_client: AsyncClient, lnbits_settings: Settings + user_alan: User, http_client: AsyncClient, settings: Settings ): # exclude 'user_id_only' - lnbits_settings.auth_allowed_methods = [AuthMethods.username_and_password.value] + settings.auth_allowed_methods = [AuthMethods.username_and_password.value] response = await http_client.post("/api/v1/auth/usr", json={"usr": user_alan.id}) assert response.status_code == 401, "Login method not allowed." assert response.json().get("detail") == "Login by 'User ID' not allowed." - lnbits_settings.auth_allowed_methods = AuthMethods.all() + settings.auth_allowed_methods = AuthMethods.all() response = await http_client.post("/api/v1/auth/usr", json={"usr": user_alan.id}) assert response.status_code == 200, "Login with 'usr' allowed." @@ -83,7 +83,7 @@ async def test_login_usr_not_allowed( @pytest.mark.asyncio async def test_login_alan_username_password_ok( - user_alan: User, http_client: AsyncClient, lnbits_settings: Settings + user_alan: User, http_client: AsyncClient, settings: Settings ): response = await http_client.post( "/api/v1/auth", json={"username": user_alan.username, "password": "secret1234"} @@ -93,7 +93,7 @@ async def test_login_alan_username_password_ok( access_token = response.json().get("access_token") assert access_token is not None - payload: dict = jwt.decode(access_token, lnbits_settings.auth_secret_key, ["HS256"]) + payload: dict = jwt.decode(access_token, settings.auth_secret_key, ["HS256"]) access_token_payload = AccessTokenPayload(**payload) assert access_token_payload.sub == "alan", "Subject is Alan." @@ -142,10 +142,10 @@ async def test_login_alan_password_nok(user_alan: User, http_client: AsyncClient @pytest.mark.asyncio async def test_login_username_password_not_allowed( - user_alan: User, http_client: AsyncClient, lnbits_settings: Settings + user_alan: User, http_client: AsyncClient, settings: Settings ): # exclude 'username_password' - lnbits_settings.auth_allowed_methods = [AuthMethods.user_id_only.value] + settings.auth_allowed_methods = [AuthMethods.user_id_only.value] response = await http_client.post( "/api/v1/auth", json={"username": user_alan.username, "password": "secret1234"} @@ -156,7 +156,7 @@ async def test_login_username_password_not_allowed( response.json().get("detail") == "Login by 'Username and Password' not allowed." ) - lnbits_settings.auth_allowed_methods = AuthMethods.all() + settings.auth_allowed_methods = AuthMethods.all() response = await http_client.post( "/api/v1/auth", json={"username": user_alan.username, "password": "secret1234"} @@ -167,7 +167,7 @@ async def test_login_username_password_not_allowed( @pytest.mark.asyncio async def test_login_alan_change_auth_secret_key( - user_alan: User, http_client: AsyncClient, lnbits_settings: Settings + user_alan: User, http_client: AsyncClient, settings: Settings ): response = await http_client.post( "/api/v1/auth", json={"username": user_alan.username, "password": "secret1234"} @@ -177,9 +177,9 @@ async def test_login_alan_change_auth_secret_key( access_token = response.json().get("access_token") assert access_token is not None - initial_auth_secret_key = lnbits_settings.auth_secret_key + initial_auth_secret_key = settings.auth_secret_key - lnbits_settings.auth_secret_key = shortuuid.uuid() + settings.auth_secret_key = shortuuid.uuid() response = await http_client.get( "/api/v1/auth", headers={"Authorization": f"Bearer {access_token}"} @@ -187,7 +187,7 @@ async def test_login_alan_change_auth_secret_key( assert response.status_code == 401, "Access token not valid anymore." assert response.json().get("detail") == "Invalid access token." - lnbits_settings.auth_secret_key = initial_auth_secret_key + settings.auth_secret_key = initial_auth_secret_key response = await http_client.get( "/api/v1/auth", headers={"Authorization": f"Bearer {access_token}"} @@ -326,7 +326,7 @@ async def test_register_bad_email(http_client: AsyncClient): ################################ CHANGE PASSWORD ################################ @pytest.mark.asyncio -async def test_change_password_ok(http_client: AsyncClient, lnbits_settings: Settings): +async def test_change_password_ok(http_client: AsyncClient, settings: Settings): tiny_id = shortuuid.uuid()[:8] response = await http_client.post( "/api/v1/auth/register", @@ -342,7 +342,7 @@ async def test_change_password_ok(http_client: AsyncClient, lnbits_settings: Set access_token = response.json().get("access_token") assert access_token is not None - payload: dict = jwt.decode(access_token, lnbits_settings.auth_secret_key, ["HS256"]) + payload: dict = jwt.decode(access_token, settings.auth_secret_key, ["HS256"]) access_token_payload = AccessTokenPayload(**payload) response = await http_client.put( @@ -447,7 +447,7 @@ async def test_alan_change_password_different_user( @pytest.mark.asyncio async def test_alan_change_password_auth_threshold_expired( - user_alan: User, http_client: AsyncClient, lnbits_settings: Settings + user_alan: User, http_client: AsyncClient, settings: Settings ): response = await http_client.post("/api/v1/auth/usr", json={"usr": user_alan.id}) @@ -456,7 +456,7 @@ async def test_alan_change_password_auth_threshold_expired( access_token = response.json().get("access_token") assert access_token is not None - lnbits_settings.auth_credetials_update_threshold = 1 + settings.auth_credetials_update_threshold = 1 time.sleep(1.1) response = await http_client.put( "/api/v1/auth/password", @@ -482,7 +482,7 @@ async def test_alan_change_password_auth_threshold_expired( @pytest.mark.asyncio -async def test_register_nostr_ok(http_client: AsyncClient, lnbits_settings: Settings): +async def test_register_nostr_ok(http_client: AsyncClient, settings: Settings): event = {**nostr_event} event["created_at"] = int(time.time()) @@ -498,7 +498,7 @@ async def test_register_nostr_ok(http_client: AsyncClient, lnbits_settings: Sett access_token = response.json().get("access_token") assert access_token is not None - payload: dict = jwt.decode(access_token, lnbits_settings.auth_secret_key, ["HS256"]) + payload: dict = jwt.decode(access_token, settings.auth_secret_key, ["HS256"]) access_token_payload = AccessTokenPayload(**payload) assert access_token_payload.auth_time, "Auth time should be set by server." assert ( @@ -522,11 +522,9 @@ async def test_register_nostr_ok(http_client: AsyncClient, lnbits_settings: Sett @pytest.mark.asyncio -async def test_register_nostr_not_allowed( - http_client: AsyncClient, lnbits_settings: Settings -): +async def test_register_nostr_not_allowed(http_client: AsyncClient, settings: Settings): # exclude 'nostr_auth_nip98' - lnbits_settings.auth_allowed_methods = [AuthMethods.username_and_password.value] + settings.auth_allowed_methods = [AuthMethods.username_and_password.value] response = await http_client.post( "/api/v1/auth/nostr", json={}, @@ -535,7 +533,7 @@ async def test_register_nostr_not_allowed( assert response.status_code == 401, "User not authenticated." assert response.json().get("detail") == "Login with Nostr Auth not allowed." - lnbits_settings.auth_allowed_methods = AuthMethods.all() + settings.auth_allowed_methods = AuthMethods.all() @pytest.mark.asyncio @@ -562,10 +560,8 @@ async def test_register_nostr_bad_header(http_client: AsyncClient): @pytest.mark.asyncio -async def test_register_nostr_bad_event( - http_client: AsyncClient, lnbits_settings: Settings -): - lnbits_settings.auth_allowed_methods = AuthMethods.all() +async def test_register_nostr_bad_event(http_client: AsyncClient, settings: Settings): + settings.auth_allowed_methods = AuthMethods.all() base64_event = base64.b64encode(json.dumps(nostr_event).encode()).decode("ascii") response = await http_client.post( "/api/v1/auth/nostr", @@ -574,7 +570,7 @@ async def test_register_nostr_bad_event( assert response.status_code == 400, "Nostr event expired." assert ( response.json().get("detail") - == f"More than {lnbits_settings.auth_credetials_update_threshold}" + == f"More than {settings.auth_credetials_update_threshold}" " seconds have passed since the event was signed." ) @@ -676,9 +672,7 @@ async def test_register_nostr_bad_event_tag_menthod(http_client: AsyncClient): ################################ CHANGE PUBLIC KEY ################################ -async def test_change_pubkey_npub_ok( - http_client: AsyncClient, lnbits_settings: Settings -): +async def test_change_pubkey_npub_ok(http_client: AsyncClient, settings: Settings): tiny_id = shortuuid.uuid()[:8] response = await http_client.post( "/api/v1/auth/register", @@ -694,7 +688,7 @@ async def test_change_pubkey_npub_ok( access_token = response.json().get("access_token") assert access_token is not None - payload: dict = jwt.decode(access_token, lnbits_settings.auth_secret_key, ["HS256"]) + payload: dict = jwt.decode(access_token, settings.auth_secret_key, ["HS256"]) access_token_payload = AccessTokenPayload(**payload) private_key = secp256k1.PrivateKey(bytes.fromhex(os.urandom(32).hex())) @@ -719,7 +713,7 @@ async def test_change_pubkey_npub_ok( @pytest.mark.asyncio async def test_change_pubkey_ok( - http_client: AsyncClient, user_alan: User, lnbits_settings: Settings + http_client: AsyncClient, user_alan: User, settings: Settings ): tiny_id = shortuuid.uuid()[:8] response = await http_client.post( @@ -736,7 +730,7 @@ async def test_change_pubkey_ok( access_token = response.json().get("access_token") assert access_token is not None - payload: dict = jwt.decode(access_token, lnbits_settings.auth_secret_key, ["HS256"]) + payload: dict = jwt.decode(access_token, settings.auth_secret_key, ["HS256"]) access_token_payload = AccessTokenPayload(**payload) private_key = secp256k1.PrivateKey(bytes.fromhex(os.urandom(32).hex())) @@ -842,7 +836,7 @@ async def test_change_pubkey_other_user(http_client: AsyncClient, user_alan: Use @pytest.mark.asyncio async def test_alan_change_pubkey_auth_threshold_expired( - user_alan: User, http_client: AsyncClient, lnbits_settings: Settings + user_alan: User, http_client: AsyncClient, settings: Settings ): response = await http_client.post("/api/v1/auth/usr", json={"usr": user_alan.id}) @@ -851,7 +845,7 @@ async def test_alan_change_pubkey_auth_threshold_expired( access_token = response.json().get("access_token") assert access_token is not None - lnbits_settings.auth_credetials_update_threshold = 1 + settings.auth_credetials_update_threshold = 1 time.sleep(2.1) response = await http_client.put( "/api/v1/auth/pubkey", @@ -872,9 +866,7 @@ async def test_alan_change_pubkey_auth_threshold_expired( ################################ RESET PASSWORD ################################ @pytest.mark.asyncio -async def test_request_reset_key_ok( - http_client: AsyncClient, lnbits_settings: Settings -): +async def test_request_reset_key_ok(http_client: AsyncClient, settings: Settings): tiny_id = shortuuid.uuid()[:8] response = await http_client.post( "/api/v1/auth/register", @@ -890,7 +882,7 @@ async def test_request_reset_key_ok( access_token = response.json().get("access_token") assert access_token is not None - payload: dict = jwt.decode(access_token, lnbits_settings.auth_secret_key, ["HS256"]) + payload: dict = jwt.decode(access_token, settings.auth_secret_key, ["HS256"]) access_token_payload = AccessTokenPayload(**payload) assert access_token_payload.usr, "User id set." @@ -947,10 +939,10 @@ async def test_request_reset_key_user_not_found(http_client: AsyncClient): @pytest.mark.asyncio async def test_reset_username_password_not_allowed( - http_client: AsyncClient, lnbits_settings: Settings + http_client: AsyncClient, settings: Settings ): # exclude 'username_password' - lnbits_settings.auth_allowed_methods = [AuthMethods.user_id_only.value] + settings.auth_allowed_methods = [AuthMethods.user_id_only.value] user_id = "926abb2ab59a48ebb2485bcceb58d05e" reset_key = await api_users_reset_password(user_id) @@ -964,7 +956,7 @@ async def test_reset_username_password_not_allowed( "password_repeat": "secret0000", }, ) - lnbits_settings.auth_allowed_methods = AuthMethods.all() + settings.auth_allowed_methods = AuthMethods.all() assert response.status_code == 401, "Login method not allowed." assert ( @@ -1010,13 +1002,13 @@ async def test_reset_username_password_bad_key(http_client: AsyncClient): @pytest.mark.asyncio async def test_reset_password_auth_threshold_expired( - user_alan: User, http_client: AsyncClient, lnbits_settings: Settings + user_alan: User, http_client: AsyncClient, settings: Settings ): reset_key = await api_users_reset_password(user_alan.id) assert reset_key, "Reset key created." - lnbits_settings.auth_credetials_update_threshold = 1 + settings.auth_credetials_update_threshold = 1 time.sleep(1.1) response = await http_client.put( "/api/v1/auth/reset", diff --git a/tests/conftest.py b/tests/conftest.py index d55402e5d..61af04c73 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -23,45 +23,41 @@ from lnbits.core.crud import ( get_user, update_payment_status, ) -from lnbits.core.models import Account, CreateInvoice, PaymentState +from lnbits.core.models import Account, CreateInvoice, PaymentState, User from lnbits.core.services import create_user_account, update_wallet_balance from lnbits.core.views.payment_api import api_payments_create_invoice from lnbits.db import DB_TYPE, SQLITE, Database -from lnbits.settings import AuthMethods, Settings, settings +from lnbits.settings import AuthMethods, Settings +from lnbits.settings import settings as lnbits_settings from tests.helpers import ( get_random_invoice_data, ) -# override settings for tests -settings.lnbits_admin_extensions = [] -settings.lnbits_data_folder = "./tests/data" -settings.lnbits_admin_ui = True -settings.lnbits_extensions_default_install = [] -settings.lnbits_extensions_deactivate_all = True + @pytest_asyncio.fixture(scope="session") -def lnbits_settings(): - return settings +def settings(): + # override settings for tests + lnbits_settings.lnbits_admin_extensions = [] + lnbits_settings.lnbits_data_folder = "./tests/data" + lnbits_settings.lnbits_admin_ui = True + lnbits_settings.lnbits_extensions_default_install = [] + lnbits_settings.lnbits_extensions_deactivate_all = True + + return lnbits_settings @pytest.fixture(autouse=True) -def run_before_and_after_tests(lnbits_settings: Settings): +def run_before_and_after_tests(settings: Settings): """Fixture to execute asserts before and after a test is run""" ##### BEFORE TEST RUN ##### - lnbits_settings.lnbits_allow_new_accounts = True - lnbits_settings.lnbits_allowed_users = [] - lnbits_settings.auth_allowed_methods = AuthMethods.all() - lnbits_settings.auth_credetials_update_threshold = 120 - lnbits_settings.lnbits_reserve_fee_percent = 1 - lnbits_settings.lnbits_reserve_fee_min = 2000 - lnbits_settings.lnbits_service_fee = 0 - lnbits_settings.lnbits_wallet_limit_daily_max_withdraw = 0 + _settings_cleanup(settings) yield # this is where the testing happens - ##### AFTER TEST RUN ##### + _settings_cleanup(settings) @pytest_asyncio.fixture(scope="session") @@ -73,7 +69,7 @@ def event_loop(): # use session scope to run once before and once after all tests @pytest_asyncio.fixture(scope="session") -async def app(): +async def app(settings: Settings): app = create_app() async with LifespanManager(app) as manager: settings.first_install = False @@ -81,7 +77,7 @@ async def app(): @pytest_asyncio.fixture(scope="session") -async def client(app): +async def client(app, settings: Settings): url = f"http://{settings.host}:{settings.port}" async with AsyncClient(transport=ASGITransport(app=app), base_url=url) as client: yield client @@ -89,7 +85,7 @@ async def client(app): # function scope @pytest_asyncio.fixture(scope="function") -async def http_client(app): +async def http_client(app, settings: Settings): url = f"http://{settings.host}:{settings.port}" async with AsyncClient(transport=ASGITransport(app=app), base_url=url) as client: @@ -155,7 +151,7 @@ async def to_user(): @pytest.fixture() -def from_super_user(from_user): +def from_super_user(from_user: User, settings: Settings): prev = settings.super_user settings.super_user = from_user.id yield from_user @@ -163,7 +159,7 @@ def from_super_user(from_user): @pytest_asyncio.fixture(scope="session") -async def superuser(): +async def superuser(settings: Settings): account = await get_account(settings.super_user) assert account, "Superuser not found" user = await get_user(account) @@ -259,3 +255,14 @@ async def fake_payments(client, adminkey_headers_from): params = {"time[ge]": ts, "time[le]": time()} return fake_data, params + + +def _settings_cleanup(settings: Settings): + settings.lnbits_allow_new_accounts = True + settings.lnbits_allowed_users = [] + settings.auth_allowed_methods = AuthMethods.all() + settings.auth_credetials_update_threshold = 120 + settings.lnbits_reserve_fee_percent = 1 + settings.lnbits_reserve_fee_min = 2000 + settings.lnbits_service_fee = 0 + settings.lnbits_wallet_limit_daily_max_withdraw = 0 diff --git a/tests/unit/test_services_fees.py b/tests/unit/test_services_fees.py index 14e7ea753..713078b8f 100644 --- a/tests/unit/test_services_fees.py +++ b/tests/unit/test_services_fees.py @@ -5,7 +5,7 @@ from lnbits.core.services import ( fee_reserve_total, service_fee, ) -from lnbits.settings import settings +from lnbits.settings import Settings @pytest.mark.asyncio @@ -15,7 +15,7 @@ async def test_fee_reserve_internal(): @pytest.mark.asyncio -async def test_fee_reserve_min(): +async def test_fee_reserve_min(settings: Settings): settings.lnbits_reserve_fee_percent = 2 settings.lnbits_reserve_fee_min = 500 fee = fee_reserve(10000) @@ -23,7 +23,7 @@ async def test_fee_reserve_min(): @pytest.mark.asyncio -async def test_fee_reserve_percent(): +async def test_fee_reserve_percent(settings: Settings): settings.lnbits_reserve_fee_percent = 1 settings.lnbits_reserve_fee_min = 100 fee = fee_reserve(100000) @@ -31,14 +31,14 @@ async def test_fee_reserve_percent(): @pytest.mark.asyncio -async def test_service_fee_no_wallet(): +async def test_service_fee_no_wallet(settings: Settings): settings.lnbits_service_fee_wallet = "" fee = service_fee(10000) assert fee == 0 @pytest.mark.asyncio -async def test_service_fee_internal(): +async def test_service_fee_internal(settings: Settings): settings.lnbits_service_fee_wallet = "wallet_id" settings.lnbits_service_fee_ignore_internal = True fee = service_fee(10000, internal=True) @@ -46,7 +46,7 @@ async def test_service_fee_internal(): @pytest.mark.asyncio -async def test_service_fee(): +async def test_service_fee(settings: Settings): settings.lnbits_service_fee_wallet = "wallet_id" settings.lnbits_service_fee = 2 fee = service_fee(10000) @@ -54,7 +54,7 @@ async def test_service_fee(): @pytest.mark.asyncio -async def test_service_fee_max(): +async def test_service_fee_max(settings: Settings): settings.lnbits_service_fee_wallet = "wallet_id" settings.lnbits_service_fee = 2 settings.lnbits_service_fee_max = 199 @@ -63,7 +63,7 @@ async def test_service_fee_max(): @pytest.mark.asyncio -async def test_fee_reserve_total(): +async def test_fee_reserve_total(settings: Settings): settings.lnbits_reserve_fee_percent = 1 settings.lnbits_reserve_fee_min = 100 settings.lnbits_service_fee = 2 diff --git a/tests/unit/test_services_wallet_limit.py b/tests/unit/test_services_wallet_limit.py index 91c03c73d..5b419eddd 100644 --- a/tests/unit/test_services_wallet_limit.py +++ b/tests/unit/test_services_wallet_limit.py @@ -1,11 +1,11 @@ import pytest from lnbits.core.services import check_wallet_daily_withdraw_limit -from lnbits.settings import settings +from lnbits.settings import Settings @pytest.mark.asyncio -async def test_no_wallet_limit(): +async def test_no_wallet_limit(settings: Settings): settings.lnbits_wallet_limit_daily_max_withdraw = 0 result = await check_wallet_daily_withdraw_limit( conn=None, wallet_id="333333", amount_msat=0 @@ -15,7 +15,7 @@ async def test_no_wallet_limit(): @pytest.mark.asyncio -async def test_wallet_limit_but_no_payments(): +async def test_wallet_limit_but_no_payments(settings: Settings): settings.lnbits_wallet_limit_daily_max_withdraw = 5 result = await check_wallet_daily_withdraw_limit( conn=None, wallet_id="333333", amount_msat=0 @@ -25,7 +25,7 @@ async def test_wallet_limit_but_no_payments(): @pytest.mark.asyncio -async def test_no_wallet_spend_allowed(): +async def test_no_wallet_spend_allowed(settings: Settings): settings.lnbits_wallet_limit_daily_max_withdraw = -1 with pytest.raises(