[test] create unit-test framework for RPC wallets (#2396)

---------

Co-authored-by: dni  <office@dnilabs.com>
This commit is contained in:
Vlad Stan 2024-04-15 18:24:28 +03:00 committed by GitHub
parent b145bff566
commit 69ce0e565b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 2128 additions and 270 deletions

View file

@ -1,4 +1,3 @@
import importlib
import json
from typing import Dict, Union
from urllib.parse import urlencode
@ -7,16 +6,15 @@ import pytest
from pytest_httpserver import HTTPServer
from werkzeug.wrappers import Response
from lnbits.core.models import BaseWallet
from tests.helpers import (
FundingSourceConfig,
Mock,
from tests.wallets.fixtures.models import Mock
from tests.wallets.helpers import (
WalletTest,
rest_wallet_fixtures_from_json,
build_test_id,
check_assertions,
load_funding_source,
wallet_fixtures_from_json,
)
wallets_module = importlib.import_module("lnbits.wallets")
# todo:
# - tests for extra fields
# - tests for paid_invoices_stream
@ -29,14 +27,10 @@ def httpserver_listen_address():
return ("127.0.0.1", 8555)
def build_test_id(test: WalletTest):
return f"{test.funding_source}.{test.function}({test.description})"
@pytest.mark.asyncio
@pytest.mark.parametrize(
"test_data",
rest_wallet_fixtures_from_json("tests/wallets/fixtures.json"),
wallet_fixtures_from_json("tests/wallets/fixtures/json/fixtures_rest.json"),
ids=build_test_id,
)
async def test_rest_wallet(httpserver: HTTPServer, test_data: WalletTest):
@ -46,8 +40,8 @@ async def test_rest_wallet(httpserver: HTTPServer, test_data: WalletTest):
for mock in test_data.mocks:
_apply_mock(httpserver, mock)
wallet = _load_funding_source(test_data.funding_source)
await _check_assertions(wallet, test_data)
wallet = load_funding_source(test_data.funding_source)
await check_assertions(wallet, test_data)
def _apply_mock(httpserver: HTTPServer, mock: Mock):
@ -65,6 +59,8 @@ def _apply_mock(httpserver: HTTPServer, mock: Mock):
if mock.query_params:
request_data["query_string"] = mock.query_params
assert mock.uri, "Missing URI for HTTP mock."
assert mock.method, "Missing method for HTTP mock."
req = httpserver.expect_request(
uri=mock.uri,
headers=mock.headers,
@ -84,60 +80,3 @@ def _apply_mock(httpserver: HTTPServer, mock: Mock):
respond_with = f"respond_with_{response_type}"
getattr(req, respond_with)(server_response)
async def _check_assertions(wallet, _test_data: WalletTest):
test_data = _test_data.dict()
tested_func = _test_data.function
call_params = _test_data.call_params
if "expect" in test_data:
await _assert_data(wallet, tested_func, call_params, _test_data.expect)
# if len(_test_data.mocks) == 0:
# # all calls should fail after this method is called
# await wallet.cleanup()
# # same behaviour expected is server canot be reached
# # or if the connection was closed
# await _assert_data(wallet, tested_func, call_params, _test_data.expect)
elif "expect_error" in test_data:
await _assert_error(wallet, tested_func, call_params, _test_data.expect_error)
else:
assert False, "Expected outcome not specified"
async def _assert_data(wallet, tested_func, call_params, expect):
resp = await getattr(wallet, tested_func)(**call_params)
for key in expect:
received = getattr(resp, key)
expected = expect[key]
assert (
getattr(resp, key) == expect[key]
), f"""Field "{key}". Received: "{received}". Expected: "{expected}"."""
async def _assert_error(wallet, tested_func, call_params, expect_error):
error_module = importlib.import_module(expect_error["module"])
error_class = getattr(error_module, expect_error["class"])
with pytest.raises(error_class) as e_info:
await getattr(wallet, tested_func)(**call_params)
assert e_info.match(expect_error["message"])
def _load_funding_source(funding_source: FundingSourceConfig) -> BaseWallet:
custom_settings = funding_source.settings | {"user_agent": "LNbits/Tests"}
original_settings = {}
settings = getattr(wallets_module, "settings")
for s in custom_settings:
original_settings[s] = getattr(settings, s)
setattr(settings, s, custom_settings[s])
fs_instance: BaseWallet = getattr(wallets_module, funding_source.wallet_class)()
# rollback settings (global variable)
for s in original_settings:
setattr(settings, s, original_settings[s])
return fs_instance