From 3815368a54c432f60667e0e708a02aa46f4fe50d Mon Sep 17 00:00:00 2001 From: everoddandeven Date: Sat, 31 Jan 2026 17:19:06 +0100 Subject: [PATCH] Improve tests code * Refactory interface tests * Setup test fixtures for wallets and daemon * Setup marker `not_supported` in `conftest.py` * Setup `unit` and `integration` test markers * Fix `py_monero_wallet` bindings --- .github/workflows/test.yml | 4 +- conftest.py | 23 + pytest.ini | 4 + src/cpp/py_monero.cpp | 26 +- src/cpp/wallet/py_monero_wallet.h | 2 +- tests/test_monero_connection_manager.py | 4 +- tests/test_monero_daemon_interface.py | 593 +++++------ tests/test_monero_daemon_rpc.py | 336 +++---- tests/test_monero_rpc_connection.py | 5 +- tests/test_monero_utils.py | 2 + tests/test_monero_wallet_common.py | 358 +++---- tests/test_monero_wallet_full.py | 76 +- tests/test_monero_wallet_interface.py | 1213 ++++++++++------------- tests/test_monero_wallet_keys.py | 457 ++++----- tests/test_monero_wallet_rpc.py | 117 ++- tests/utils/assert_utils.py | 8 - tests/utils/test_utils.py | 7 - 17 files changed, 1436 insertions(+), 1799 deletions(-) create mode 100644 conftest.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bbe18f1..a95039e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -235,11 +235,11 @@ jobs: done cd .. - - name: Run tests + - name: Run unit tests shell: bash env: IN_CONTAINER: "true" PYTHONPATH: ${{ github.workspace }}/dist PATH: "${{ github.workspace }}/dist:/usr/bin:$PATH" run: | - python -m pytest + python -m pytest -m unit diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000..925138f --- /dev/null +++ b/conftest.py @@ -0,0 +1,23 @@ +import pytest + +from monero import MoneroError + +def pytest_runtest_call(item: pytest.Item): + # get not_supported marked + marker = item.get_closest_marker("not_supported") + if marker is None: + # marked not found + return + + try: + # run test + item.runtest() + except MoneroError as e: + e_str = str(e).lower() + if "not supported" in e_str or "does not support" in e_str: + # Ok + pytest.xfail(str(e)) + raise + else: + # fail test + pytest.fail("Expected test to fail") diff --git a/pytest.ini b/pytest.ini index e1b7e81..51ccf6f 100644 --- a/pytest.ini +++ b/pytest.ini @@ -7,3 +7,7 @@ log_cli_level = INFO console_output_style = progress testpaths = tests +markers = + unit: fast unit tests, no external dependencies + integration: slow integration tests, requires external services + not_supported: expects not supported error diff --git a/src/cpp/py_monero.cpp b/src/cpp/py_monero.cpp index 976cf43..328f84e 100644 --- a/src/cpp/py_monero.cpp +++ b/src/cpp/py_monero.cpp @@ -1538,19 +1538,7 @@ PYBIND11_MODULE(monero, m) { MONERO_CATCH_AND_RETHROW(self.remove_listener(listener)); }, py::arg("listener")) .def("get_listeners", [](PyMoneroWallet& self) { - try { - std::set listeners = self.get_listeners(); - std::vector> result(listeners.size()); - - for(auto listener : listeners) { - result.emplace_back(listener); - } - - return result; - } - catch (const std::exception& e) { - throw py::value_error(e.what()); - } + MONERO_CATCH_AND_RETHROW(self.get_listeners()); }) .def("sync", [](PyMoneroWallet& self) { MONERO_CATCH_AND_RETHROW(self.sync()); @@ -1800,9 +1788,13 @@ PYBIND11_MODULE(monero, m) { MONERO_CATCH_AND_RETHROW(self.parse_payment_uri(uri)); }, py::arg("uri")) .def("get_attribute", [](PyMoneroWallet& self, const std::string& key) { - std::string val; - self.get_attribute(key, val); - return val; + try { + std::string val; + self.get_attribute(key, val); + return val; + } catch (const std::exception& ex) { + throw PyMoneroError(ex.what()); + } }, py::arg("key")) .def("set_attribute", [](PyMoneroWallet& self, const std::string& key, const std::string& val) { MONERO_CATCH_AND_RETHROW(self.set_attribute(key, val)); @@ -1856,7 +1848,7 @@ PYBIND11_MODULE(monero, m) { MONERO_CATCH_AND_RETHROW(self.save()); }) .def("close", [](monero::monero_wallet& self, bool save) { - self.close(save); + MONERO_CATCH_AND_RETHROW(self.close(save)); }, py::arg("save") = false); // monero_wallet_keys diff --git a/src/cpp/wallet/py_monero_wallet.h b/src/cpp/wallet/py_monero_wallet.h index cf6b69f..e756a1b 100644 --- a/src/cpp/wallet/py_monero_wallet.h +++ b/src/cpp/wallet/py_monero_wallet.h @@ -179,7 +179,7 @@ class PyMoneroWallet : public monero::monero_wallet { void remove_listener(monero_wallet_listener& listener) override { m_listeners.erase(&listener); } - + std::set get_listeners() override { return m_listeners; } diff --git a/tests/test_monero_connection_manager.py b/tests/test_monero_connection_manager.py index cb55025..aa51b0a 100644 --- a/tests/test_monero_connection_manager.py +++ b/tests/test_monero_connection_manager.py @@ -10,8 +10,10 @@ logger: logging.Logger = logging.getLogger("TestMoneroConnectionManager") # TODO enable connection manager tests -@pytest.mark.skipif(True, reason="TODO") +@pytest.mark.skip(reason="TODO") +@pytest.mark.integration class TestMoneroConnectionManager: + """Connection manager integration tests""" @pytest.fixture(autouse=True) def setup_and_teardown(self, request: pytest.FixtureRequest): diff --git a/tests/test_monero_daemon_interface.py b/tests/test_monero_daemon_interface.py index befd084..2b355c0 100644 --- a/tests/test_monero_daemon_interface.py +++ b/tests/test_monero_daemon_interface.py @@ -1,25 +1,15 @@ import pytest import logging -from typing import Optional -from monero import ( - MoneroDaemon, MoneroBan -) -from utils import AssertUtils +from monero import MoneroDaemon, MoneroBan logger: logging.Logger = logging.getLogger("TestMoneroDaemonInterface") # Test calls to MoneroDaemon interface +@pytest.mark.unit class TestMoneroDaemonInterface: - - _daemon: Optional[MoneroDaemon] = None - - def _get_daemon(self) -> MoneroDaemon: - if self._daemon is None: - self._daemon = MoneroDaemon() - - return self._daemon + """Daemon interface bindings unit tests""" @pytest.fixture(autouse=True) def setup_and_teardown(self, request: pytest.FixtureRequest): @@ -27,342 +17,249 @@ def setup_and_teardown(self, request: pytest.FixtureRequest): yield logger.info(f"After {request.node.name}") # type: ignore + @pytest.fixture(scope="class") + def daemon(self) -> MoneroDaemon: + """Test rpc daemon instance""" + return MoneroDaemon() + #region Tests - # Test general node info - def test_info(self) -> None: - daemon = self._get_daemon() - - try: - daemon.get_version() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.is_trusted() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_fee_estimate() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_info() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_hard_fork_info - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_alt_block_hashes() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_alt_chains() - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test node sync info - def test_sync_info(self) -> None: - daemon = self._get_daemon() - - try: - daemon.get_height() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_last_block_header() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_sync_info() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.wait_for_next_block_header() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_key_image_spent_status("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_key_image_spent_statuses([]) - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test blockchain - def test_blocks(self) -> None: - daemon = self._get_daemon() - - try: - daemon.get_block_hash(1) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_block_template("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_block_header_by_hash("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_block_header_by_height(1) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_block_headers_by_range(1, 100) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_block_by_hash("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_blocks_by_hash([], 0, False) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_block_by_height(1) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_blocks_by_height([1]) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_blocks_by_range(1, 100) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_blocks_by_range_chunked(1, 100) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_block_hashes([], 0) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.submit_block("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.submit_blocks([]) - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test txs - def test_txs(self) -> None: - daemon = self._get_daemon() - - try: - daemon.get_tx("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_txs([]) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_tx_hex("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_tx_hexes([]) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_miner_tx_sum(0, 100) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.submit_tx_hex("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.relay_tx_by_hash("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.relay_txs_by_hash([]) - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test tx pool - def test_tx_pool(self) -> None: - daemon = self._get_daemon() - - try: - daemon.get_tx_pool() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_tx_pool_hashes() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_tx_pool_backlog() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_tx_pool_stats() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.flush_tx_pool() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.flush_tx_pool("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.flush_tx_pool([""]) - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test outputs - def test_outputs(self) -> None: - daemon = self._get_daemon() - - try: - daemon.get_outputs([]) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_output_histogram([], 0, 100, False, 1000) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_output_distribution([], False, 0, 1) - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test network and peers - def test_network(self) -> None: - daemon = self._get_daemon() - - try: - daemon.get_peers() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_known_peers() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.set_outgoing_peer_limit(1000) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.set_incoming_peer_limit(10000) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_peer_bans() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.set_peer_bans([]) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.set_peer_ban(MoneroBan()) - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test mining - def test_mining(self) -> None: - daemon = self._get_daemon() - - try: - daemon.start_mining("", 1, False, False) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.stop_mining() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.get_mining_status() - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test other utilities - def test_utilities(self) -> None: - daemon = self._get_daemon() - - try: - daemon.prune_blockchain(False) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.check_for_update() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.download_update() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.download_update("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - daemon.wait_for_next_block_header() - except Exception as e: - AssertUtils.assert_not_supported(e) + # Test interface calls + + @pytest.mark.not_supported + def test_get_version(self, daemon: MoneroDaemon) -> None: + daemon.get_version() + + @pytest.mark.not_supported + def test_is_trusted(self, daemon: MoneroDaemon) -> None: + daemon.is_trusted() + + @pytest.mark.not_supported + def test_get_fee_estimate(self, daemon: MoneroDaemon) -> None: + daemon.get_fee_estimate() + + @pytest.mark.not_supported + def test_get_info(self, daemon: MoneroDaemon) -> None: + daemon.get_info() + + @pytest.mark.not_supported + def test_get_hard_fork_info(self, daemon: MoneroDaemon) -> None: + daemon.get_hard_fork_info() + + @pytest.mark.not_supported + def test_get_alt_block_hashes(self, daemon: MoneroDaemon) -> None: + daemon.get_alt_block_hashes() + + @pytest.mark.not_supported + def test_get_alt_chains(self, daemon: MoneroDaemon) -> None: + daemon.get_alt_chains() + + @pytest.mark.not_supported + def test_get_sync_info(self, daemon: MoneroDaemon) -> None: + daemon.get_sync_info() + + @pytest.mark.not_supported + def test_get_height(self, daemon: MoneroDaemon) -> None: + daemon.get_height() + + @pytest.mark.not_supported + def test_get_last_block_header(self, daemon: MoneroDaemon) -> None: + daemon.get_last_block_header() + + @pytest.mark.not_supported + def test_wait_for_next_block_header(self, daemon: MoneroDaemon) -> None: + daemon.wait_for_next_block_header() + + @pytest.mark.not_supported + def test_get_key_image_spent_status(self, daemon: MoneroDaemon) -> None: + daemon.get_key_image_spent_status("") + + @pytest.mark.not_supported + def test_get_key_image_spent_statuses(self, daemon: MoneroDaemon) -> None: + daemon.get_key_image_spent_statuses([]) + + @pytest.mark.not_supported + def test_get_block_hash(self, daemon: MoneroDaemon) -> None: + daemon.get_block_hash(1) + + @pytest.mark.not_supported + def test_get_block_template(self, daemon: MoneroDaemon) -> None: + daemon.get_block_template("") + + @pytest.mark.not_supported + def test_get_block_header_by_hash(self, daemon: MoneroDaemon) -> None: + daemon.get_block_header_by_hash("") + + @pytest.mark.not_supported + def test_get_block_header_by_height(self, daemon: MoneroDaemon) -> None: + daemon.get_block_header_by_height(1) + + @pytest.mark.not_supported + def test_block_headers_by_range(self, daemon: MoneroDaemon) -> None: + daemon.get_block_headers_by_range(1, 100) + + @pytest.mark.not_supported + def test_get_block_by_hash(self, daemon: MoneroDaemon) -> None: + daemon.get_block_by_hash("") + + @pytest.mark.not_supported + def test_blocks_by_hash(self, daemon: MoneroDaemon) -> None: + daemon.get_blocks_by_hash([], 0, False) + + @pytest.mark.not_supported + def test_get_block_by_height(self, daemon: MoneroDaemon) -> None: + daemon.get_block_by_height(1) + + @pytest.mark.not_supported + def test_get_blocks_by_height(self, daemon: MoneroDaemon) -> None: + daemon.get_blocks_by_height([1]) + + @pytest.mark.not_supported + def test_get_blocks_by_range(self, daemon: MoneroDaemon) -> None: + daemon.get_blocks_by_range(1, 100) + + @pytest.mark.not_supported + def test_get_blocks_by_range_chunked(self, daemon: MoneroDaemon) -> None: + daemon.get_blocks_by_range_chunked(1, 100) + + @pytest.mark.not_supported + def test_get_block_hashes(self, daemon: MoneroDaemon) -> None: + daemon.get_block_hashes([], 0) + + @pytest.mark.not_supported + def test_submit_block(self, daemon: MoneroDaemon) -> None: + daemon.submit_block("") + + @pytest.mark.not_supported + def test_submit_blocks(self, daemon: MoneroDaemon) -> None: + daemon.submit_blocks([]) + + @pytest.mark.not_supported + def test_get_tx(self, daemon: MoneroDaemon) -> None: + daemon.get_tx("") + + @pytest.mark.not_supported + def test_get_txs(self, daemon: MoneroDaemon) -> None: + daemon.get_txs([]) + + @pytest.mark.not_supported + def test_get_tx_hex(self, daemon: MoneroDaemon) -> None: + daemon.get_tx_hex("") + + @pytest.mark.not_supported + def test_get_tx_hexes(self, daemon: MoneroDaemon) -> None: + daemon.get_tx_hexes([]) + + @pytest.mark.not_supported + def test_get_miner_tx_sum(self, daemon: MoneroDaemon) -> None: + daemon.get_miner_tx_sum(0, 100) + + @pytest.mark.not_supported + def test_submit_tx_hex(self, daemon: MoneroDaemon) -> None: + daemon.submit_tx_hex("") + + @pytest.mark.not_supported + def test_relay_tx_by_hash(self, daemon: MoneroDaemon) -> None: + daemon.relay_tx_by_hash("") + + @pytest.mark.not_supported + def test_relay_txs_by_hash(self, daemon: MoneroDaemon) -> None: + daemon.relay_txs_by_hash([]) + + @pytest.mark.not_supported + def test_get_tx_pool(self, daemon: MoneroDaemon) -> None: + daemon.get_tx_pool() + + @pytest.mark.not_supported + def test_get_tx_pool_hashes(self, daemon: MoneroDaemon) -> None: + daemon.get_tx_pool_hashes() + + @pytest.mark.not_supported + def test_get_tx_pool_backlog(self, daemon: MoneroDaemon) -> None: + daemon.get_tx_pool_backlog() + + @pytest.mark.not_supported + def test_get_tx_pool_stats(self, daemon: MoneroDaemon) -> None: + daemon.get_tx_pool_stats() + + @pytest.mark.not_supported + def test_flush_tx_pool_1(self, daemon: MoneroDaemon) -> None: + daemon.flush_tx_pool() + + @pytest.mark.not_supported + def test_flush_tx_pool_2(self, daemon: MoneroDaemon) -> None: + daemon.flush_tx_pool("") + + @pytest.mark.not_supported + def test_flush_tx_pool_3(self, daemon: MoneroDaemon) -> None: + daemon.flush_tx_pool([""]) + + @pytest.mark.not_supported + def test_get_outputs(self, daemon: MoneroDaemon) -> None: + daemon.get_outputs([]) + + @pytest.mark.not_supported + def test_get_output_histogram(self, daemon: MoneroDaemon) -> None: + daemon.get_output_histogram([], 0, 100, False, 1000) + + @pytest.mark.not_supported + def test_output_distribution(self, daemon: MoneroDaemon) -> None: + daemon.get_output_distribution([], False, 0, 1) + + @pytest.mark.not_supported + def test_get_peers(self, daemon: MoneroDaemon) -> None: + daemon.get_peers() + + @pytest.mark.not_supported + def test_get_known_peers(self, daemon: MoneroDaemon) -> None: + daemon.get_known_peers() + + @pytest.mark.not_supported + def test_set_outgoing_peer_limit(self, daemon: MoneroDaemon) -> None: + daemon.set_outgoing_peer_limit(1000) + + @pytest.mark.not_supported + def test_set_incoming_peer_limit(self, daemon: MoneroDaemon) -> None: + daemon.set_incoming_peer_limit(10000) + + @pytest.mark.not_supported + def test_get_peer_bans(self, daemon: MoneroDaemon) -> None: + daemon.get_peer_bans() + + @pytest.mark.not_supported + def test_set_peer_bans(self, daemon: MoneroDaemon) -> None: + daemon.set_peer_bans([]) + + @pytest.mark.not_supported + def test_set_peer_ban(self, daemon: MoneroDaemon) -> None: + daemon.set_peer_ban(MoneroBan()) + + @pytest.mark.not_supported + def test_start_mining(self, daemon: MoneroDaemon) -> None: + daemon.start_mining("", 1, False, False) + + @pytest.mark.not_supported + def test_stop_mining(self, daemon: MoneroDaemon) -> None: + daemon.stop_mining() + + @pytest.mark.not_supported + def test_get_mining_status(self, daemon: MoneroDaemon) -> None: + daemon.get_mining_status() + + @pytest.mark.not_supported + def test_prune_blockchain(self, daemon: MoneroDaemon) -> None: + daemon.prune_blockchain(False) + + @pytest.mark.not_supported + def test_check_for_update(self, daemon: MoneroDaemon) -> None: + daemon.check_for_update() + + @pytest.mark.not_supported + def test_download_update_1(self, daemon: MoneroDaemon) -> None: + daemon.download_update() + + @pytest.mark.not_supported + def test_download_update_2(self, daemon: MoneroDaemon) -> None: + daemon.download_update("") #endregion diff --git a/tests/test_monero_daemon_rpc.py b/tests/test_monero_daemon_rpc.py index 73549eb..e647d08 100644 --- a/tests/test_monero_daemon_rpc.py +++ b/tests/test_monero_daemon_rpc.py @@ -5,34 +5,33 @@ from monero import ( MoneroDaemonRpc, MoneroVersion, MoneroBlockHeader, MoneroBlockTemplate, - MoneroBlock, MoneroWallet, MoneroMiningStatus, MoneroPruneResult, + MoneroBlock, MoneroMiningStatus, MoneroPruneResult, MoneroDaemonUpdateCheckResult, MoneroDaemonUpdateDownloadResult, MoneroDaemonListener, MoneroPeer, MoneroDaemonInfo, MoneroDaemonSyncInfo, MoneroHardForkInfo, MoneroAltChain, MoneroTx, MoneroSubmitTxResult, - MoneroTxPoolStats, MoneroBan, MoneroTxConfig, MoneroDestination + MoneroTxPoolStats, MoneroBan, MoneroTxConfig, MoneroDestination, + MoneroWalletRpc ) from utils import ( TestUtils as Utils, TestContext, BinaryBlockContext, MiningUtils, - OsUtils, AssertUtils, TxUtils, + AssertUtils, TxUtils, BlockUtils, GenUtils, DaemonUtils ) logger: logging.Logger = logging.getLogger("TestMoneroDaemonRpc") -@pytest.mark.skipif(OsUtils.is_windows(), reason="TODO setup test environment for windows") +@pytest.mark.integration class TestMoneroDaemonRpc: - _daemon: MoneroDaemonRpc = Utils.get_daemon_rpc() - _wallet: MoneroWallet = Utils.get_wallet_rpc() # type: ignore + """Rpc daemon integration tests""" BINARY_BLOCK_CTX: BinaryBlockContext = BinaryBlockContext() #region Fixtures @pytest.fixture(scope="class", autouse=True) def before_all(self): - if not OsUtils.is_windows(): - MiningUtils.wait_until_blockchain_ready() + MiningUtils.wait_until_blockchain_ready() @pytest.fixture(autouse=True) def setup_and_teardown(self, request: pytest.FixtureRequest): @@ -40,95 +39,105 @@ def setup_and_teardown(self, request: pytest.FixtureRequest): yield logger.info(f"After {request.node.name}") # type: ignore + @pytest.fixture(scope="class") + def daemon(self) -> MoneroDaemonRpc: + """Test rpc daemon instance""" + return Utils.get_daemon_rpc() + + @pytest.fixture(scope="class") + def wallet(self) -> MoneroWalletRpc: + """Test rpc wallet instance""" + return Utils.get_wallet_rpc() + #endregion #region Tests # Can get the daemon's version @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_version(self): - version: MoneroVersion = self._daemon.get_version() + def test_get_version(self, daemon: MoneroDaemonRpc): + version: MoneroVersion = daemon.get_version() assert version.number is not None AssertUtils.assert_true(version.number > 0) AssertUtils.assert_not_none(version.is_release) # Can indicate if it's trusted @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_is_trusted(self): - self._daemon.is_trusted() + def test_is_trusted(self, daemon: MoneroDaemonRpc): + daemon.is_trusted() # Can get the blockchain height @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_height(self): - height = self._daemon.get_height() + def test_get_height(self, daemon: MoneroDaemonRpc): + height = daemon.get_height() AssertUtils.assert_true(height > 0, "Height must be greater than 0") # Can get a block hash by height @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_block_id_by_height(self): - last_header: MoneroBlockHeader = self._daemon.get_last_block_header() + def test_get_block_id_by_height(self, daemon: MoneroDaemonRpc): + last_header: MoneroBlockHeader = daemon.get_last_block_header() assert last_header.height is not None - hash_str: str = self._daemon.get_block_hash(last_header.height) + hash_str: str = daemon.get_block_hash(last_header.height) AssertUtils.assert_not_none(hash_str) AssertUtils.assert_equals(64, len(hash_str)) # Can get a block template @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_block_template(self): - template: MoneroBlockTemplate = self._daemon.get_block_template(Utils.ADDRESS, 2) + def test_get_block_template(self, daemon: MoneroDaemonRpc): + template: MoneroBlockTemplate = daemon.get_block_template(Utils.ADDRESS, 2) DaemonUtils.test_block_template(template) # Can get the last block's header @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_last_block_header(self): - last_header: MoneroBlockHeader = self._daemon.get_last_block_header() + def test_get_last_block_header(self, daemon: MoneroDaemonRpc): + last_header: MoneroBlockHeader = daemon.get_last_block_header() BlockUtils.test_block_header(last_header, True) # Can get a block header by hash @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_block_header_by_hash(self): + def test_get_block_header_by_hash(self, daemon: MoneroDaemonRpc): # retrieve by hash of last block - last_header: MoneroBlockHeader = self._daemon.get_last_block_header() + last_header: MoneroBlockHeader = daemon.get_last_block_header() assert last_header.height is not None - hash_str: str = self._daemon.get_block_hash(last_header.height) - header: MoneroBlockHeader = self._daemon.get_block_header_by_hash(hash_str) + hash_str: str = daemon.get_block_hash(last_header.height) + header: MoneroBlockHeader = daemon.get_block_header_by_hash(hash_str) BlockUtils.test_block_header(header, True) AssertUtils.assert_equals(last_header, header) # retrieve by hash of previous to last block - hash_str = self._daemon.get_block_hash(last_header.height - 1) - header = self._daemon.get_block_header_by_hash(hash_str) + hash_str = daemon.get_block_hash(last_header.height - 1) + header = daemon.get_block_header_by_hash(hash_str) BlockUtils.test_block_header(header, True) AssertUtils.assert_equals(last_header.height - 1, header.height) # Can get a block header by height @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_block_header_by_height(self): + def test_get_block_header_by_height(self, daemon: MoneroDaemonRpc): # retrieve by height of last block - last_header: MoneroBlockHeader = self._daemon.get_last_block_header() + last_header: MoneroBlockHeader = daemon.get_last_block_header() assert last_header.height is not None - header: MoneroBlockHeader = self._daemon.get_block_header_by_height(last_header.height) + header: MoneroBlockHeader = daemon.get_block_header_by_height(last_header.height) BlockUtils.test_block_header(header, True) AssertUtils.assert_equals(last_header, header) # retrieve by height of previous to last block - header = self._daemon.get_block_header_by_height(last_header.height - 1) + header = daemon.get_block_header_by_height(last_header.height - 1) BlockUtils.test_block_header(header, True) AssertUtils.assert_equals(last_header.height - 1, header.height) # Can get block headers by range # TODO: test start with no end, vice versa, inclusivity @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_block_headers_by_range(self): + def test_get_block_headers_by_range(self, daemon: MoneroDaemonRpc): # determine start and end height based on number of blocks and how many blocks ago num_blocks = 100 num_blocks_ago = 100 - current_height = self._daemon.get_height() + current_height = daemon.get_height() start_height = current_height - num_blocks_ago end_height = current_height - (num_blocks_ago - num_blocks) - 1 # fetch headers - headers: list[MoneroBlockHeader] = self._daemon.get_block_headers_by_range(start_height, end_height) + headers: list[MoneroBlockHeader] = daemon.get_block_headers_by_range(start_height, end_height) # test headers AssertUtils.assert_equals(num_blocks, len(headers)) @@ -141,7 +150,7 @@ def test_get_block_headers_by_range(self): # Can get a block by hash @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_block_by_hash(self): + def test_get_block_by_hash(self, daemon: MoneroDaemonRpc): # test config ctx = TestContext() ctx.has_hex = True @@ -149,20 +158,20 @@ def test_get_block_by_hash(self): ctx.header_is_full = True # retrieve by hash of last block - last_header: MoneroBlockHeader = self._daemon.get_last_block_header() + last_header: MoneroBlockHeader = daemon.get_last_block_header() assert last_header.height is not None - hash_str: str = self._daemon.get_block_hash(last_header.height) - block: MoneroBlock = self._daemon.get_block_by_hash(hash_str) + hash_str: str = daemon.get_block_hash(last_header.height) + block: MoneroBlock = daemon.get_block_by_hash(hash_str) assert block.height is not None BlockUtils.test_block(block, ctx) - AssertUtils.assert_equals(self._daemon.get_block_by_height(block.height), block) + AssertUtils.assert_equals(daemon.get_block_by_height(block.height), block) assert len(block.txs) == 0, f"No block tx expected, found: {len(block.txs)}" # retrieve by hash of previous to last block - hash_str = self._daemon.get_block_hash(last_header.height - 1) - block = self._daemon.get_block_by_hash(hash_str) + hash_str = daemon.get_block_hash(last_header.height - 1) + block = daemon.get_block_by_hash(hash_str) BlockUtils.test_block(block, ctx) - AssertUtils.assert_equals(self._daemon.get_block_by_height(last_header.height - 1), block) + AssertUtils.assert_equals(daemon.get_block_by_height(last_header.height - 1), block) assert len(block.txs) == 0, f"No block tx expected, found: {len(block.txs)}" # Can get blocks by hash which includes transactions (binary) @@ -173,7 +182,7 @@ def test_get_blocks_by_hash_binary(self) -> None: # Can get a block by height @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_block_by_height(self): + def test_get_block_by_height(self, daemon: MoneroDaemonRpc): # config for testing blocks ctx = TestContext() ctx.has_hex = True @@ -181,27 +190,27 @@ def test_get_block_by_height(self): ctx.has_txs = False # retrieve by height of last block - last_header: MoneroBlockHeader = self._daemon.get_last_block_header() + last_header: MoneroBlockHeader = daemon.get_last_block_header() assert last_header.height is not None - block: MoneroBlock = self._daemon.get_block_by_height(last_header.height) + block: MoneroBlock = daemon.get_block_by_height(last_header.height) assert block.height is not None BlockUtils.test_block(block, ctx) - AssertUtils.assert_equals(self._daemon.get_block_by_height(block.height), block) + AssertUtils.assert_equals(daemon.get_block_by_height(block.height), block) # retrieve by height of previous to last block - block = self._daemon.get_block_by_height(last_header.height - 1) + block = daemon.get_block_by_height(last_header.height - 1) BlockUtils.test_block(block, ctx) AssertUtils.assert_equals(last_header.height - 1, block.height) # Can get blocks by height which includes transactions (binary) #@pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @pytest.mark.skip(reason="TODO fund wallet") - def test_get_blocks_by_height_binary(self): + def test_get_blocks_by_height_binary(self, daemon: MoneroDaemonRpc): # set number of blocks to test num_blocks = 100 # select random heights # TODO: this is horribly inefficient way of computing last 100 blocks if not shuffling - current_height: int = self._daemon.get_height() + current_height: int = daemon.get_height() all_heights: list[int] = [] i: int = 0 while i < current_height: @@ -216,7 +225,7 @@ def test_get_blocks_by_height_binary(self): i += 1 # fetch blocks - blocks: list[MoneroBlock] = self._daemon.get_blocks_by_height(heights) + blocks: list[MoneroBlock] = daemon.get_blocks_by_height(heights) # test blocks tx_found: bool = False @@ -235,45 +244,45 @@ def test_get_blocks_by_height_binary(self): # Can get blocks by range in a single request @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_blocks_by_range(self): + def test_get_blocks_by_range(self, daemon: MoneroDaemonRpc): # get height range num_blocks = 100 num_blocks_ago = 102 AssertUtils.assert_true(num_blocks > 0) AssertUtils.assert_true(num_blocks_ago >= num_blocks) - height = self._daemon.get_height() + height = daemon.get_height() AssertUtils.assert_true(height - num_blocks_ago + num_blocks - 1 < height) start_height = height - num_blocks_ago end_height = height - num_blocks_ago + num_blocks - 1 # test known start and end heights - BlockUtils.test_get_blocks_range(self._daemon, start_height, end_height, height, False, self.BINARY_BLOCK_CTX) + BlockUtils.test_get_blocks_range(daemon, start_height, end_height, height, False, self.BINARY_BLOCK_CTX) # test unspecified start - BlockUtils.test_get_blocks_range(self._daemon, None, num_blocks - 1, height, False, self.BINARY_BLOCK_CTX) + BlockUtils.test_get_blocks_range(daemon, None, num_blocks - 1, height, False, self.BINARY_BLOCK_CTX) # test unspecified end - BlockUtils.test_get_blocks_range(self._daemon, height - num_blocks - 1, None, height, False, self.BINARY_BLOCK_CTX) + BlockUtils.test_get_blocks_range(daemon, height - num_blocks - 1, None, height, False, self.BINARY_BLOCK_CTX) # Can get blocks by range using chunked requests @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_blocks_by_range_chunked(self): + def test_get_blocks_by_range_chunked(self, daemon: MoneroDaemonRpc): # get long height range - num_blocks = min(self._daemon.get_height() - 2, 1440) # test up to ~2 days of blocks + num_blocks = min(daemon.get_height() - 2, 1440) # test up to ~2 days of blocks AssertUtils.assert_true(num_blocks > 0) - height = self._daemon.get_height() + height = daemon.get_height() AssertUtils.assert_true(height - num_blocks - 1 < height) start_height = height - num_blocks end_height = height - 1 # test known start and end heights - BlockUtils.test_get_blocks_range(self._daemon, start_height, end_height, height, True, self.BINARY_BLOCK_CTX) + BlockUtils.test_get_blocks_range(daemon, start_height, end_height, height, True, self.BINARY_BLOCK_CTX) # test unspecified start - BlockUtils.test_get_blocks_range(self._daemon, None, num_blocks - 1, height, True, self.BINARY_BLOCK_CTX) + BlockUtils.test_get_blocks_range(daemon, None, num_blocks - 1, height, True, self.BINARY_BLOCK_CTX) # test unspecified end - BlockUtils.test_get_blocks_range(self._daemon, end_height - num_blocks - 1, None, height, True, self.BINARY_BLOCK_CTX) + BlockUtils.test_get_blocks_range(daemon, end_height - num_blocks - 1, None, height, True, self.BINARY_BLOCK_CTX) # Can get block hashes (binary) #@pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @@ -284,9 +293,9 @@ def test_get_block_ids_binary(self) -> None: # Can get a transaction by hash and without pruning @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_tx_by_hash(self) -> None: + def test_get_tx_by_hash(self, daemon: MoneroDaemonRpc) -> None: # fetch tx hashses to test - tx_hashes = TxUtils.get_confirmed_tx_hashes(self._daemon) + tx_hashes = TxUtils.get_confirmed_tx_hashes(daemon) # context for creating txs ctx = TestContext() @@ -296,18 +305,18 @@ def test_get_tx_by_hash(self) -> None: # fetch each tx by hash without pruning for tx_hash in tx_hashes: - tx = self._daemon.get_tx(tx_hash) + tx = daemon.get_tx(tx_hash) TxUtils.test_tx(tx, ctx) # fetch each tx by hash with pruning for tx_hash in tx_hashes: - tx = self._daemon.get_tx(tx_hash, True) + tx = daemon.get_tx(tx_hash, True) ctx.is_pruned = True TxUtils.test_tx(tx, ctx) # fetch invalid hash try: - self._daemon.get_tx("invalid tx hash") + daemon.get_tx("invalid tx hash") raise Exception("fail") except Exception as e: AssertUtils.assert_equals("Invalid transaction hash", str(e)) @@ -315,9 +324,9 @@ def test_get_tx_by_hash(self) -> None: # Can get transactions by hashes with and without pruning #@pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @pytest.mark.skip(reason="TODO fund wallet") - def test_get_txs_by_hashes(self) -> None: + def test_get_txs_by_hashes(self, daemon: MoneroDaemonRpc, wallet: MoneroWalletRpc) -> None: # fetch tx hashses to test - tx_hashes = TxUtils.get_confirmed_tx_hashes(self._daemon) + tx_hashes = TxUtils.get_confirmed_tx_hashes(daemon) assert len(tx_hashes) > 0, "No tx hashes found" # context for creating txs @@ -327,13 +336,13 @@ def test_get_txs_by_hashes(self) -> None: ctx.from_get_tx_pool = False # fetch each tx by hash without pruning - txs = self._daemon.get_txs(tx_hashes) + txs = daemon.get_txs(tx_hashes) assert len(txs) == len(tx_hashes), f"Expected len(txs) == len(tx_hashes), got {len(txs)} == {len(tx_hashes)}" for tx in txs: TxUtils.test_tx(tx, ctx) # fetch each tx by hash with pruning - txs = self._daemon.get_txs(tx_hashes, True) + txs = daemon.get_txs(tx_hashes, True) ctx.is_pruned = True assert len(txs) == len(tx_hashes), f"Expected len(txs) == len(tx_hashes), got {len(txs)} == {len(tx_hashes)}" for tx in txs: @@ -341,22 +350,22 @@ def test_get_txs_by_hashes(self) -> None: # fetch missing hash dest = MoneroDestination() - dest.address = self._wallet.get_primary_address() + dest.address = wallet.get_primary_address() dest.amount = TxUtils.MAX_FEE config = MoneroTxConfig() config.account_index = 0 config.destinations.append(dest) - tx = self._wallet.create_tx(config) + tx = wallet.create_tx(config) assert tx.hash is not None - assert self._daemon.get_tx(tx.hash) is None + assert daemon.get_tx(tx.hash) is None tx_hashes.append(tx.hash) num_txs = len(txs) - txs = self._daemon.get_txs(tx_hashes) + txs = daemon.get_txs(tx_hashes) assert num_txs == len(txs) # fetch invalid hash try: - self._daemon.get_txs(["invalid tx hash"]) + daemon.get_txs(["invalid tx hash"]) raise Exception("fail") except Exception as e: AssertUtils.assert_equals("Invalid transaction hash", str(e)) @@ -364,9 +373,8 @@ def test_get_txs_by_hashes(self) -> None: # Can get transaction pool statistics #@pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @pytest.mark.skip("TODO implement monero_wallet_rpc.get_txs()") - def test_get_tx_pool_statistics(self): - daemon = self._daemon - wallet = self._wallet + def test_get_tx_pool_statistics(self, daemon: MoneroDaemonRpc, wallet: MoneroWalletRpc): + wallet = wallet Utils.WALLET_TX_TRACKER.wait_for_wallet_txs_to_clear_pool(daemon, Utils.SYNC_PERIOD_IN_MS, [wallet]) tx_ids: list[str] = [] try: @@ -376,33 +384,31 @@ def test_get_tx_pool_statistics(self): # submit tx hex tx: MoneroTx = TxUtils.get_unrelayed_tx(wallet, i) assert tx.full_hex is not None - result: MoneroSubmitTxResult = self._daemon.submit_tx_hex(tx.full_hex, True) + result: MoneroSubmitTxResult = daemon.submit_tx_hex(tx.full_hex, True) AssertUtils.assert_true(result.is_good, json.dumps(result)) assert tx.hash is not None tx_ids.append(tx.hash) # get tx pool stats - stats: MoneroTxPoolStats = self._daemon.get_tx_pool_stats() + stats: MoneroTxPoolStats = daemon.get_tx_pool_stats() assert stats.num_txs is not None AssertUtils.assert_true(stats.num_txs > i - 1) DaemonUtils.test_tx_pool_stats(stats) i += 1 - - except Exception as e: + finally: # flush txs - self._daemon.flush_tx_pool(tx_ids) - raise e + daemon.flush_tx_pool(tx_ids) # Can get the miner tx sum @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_miner_tx_sum(self) -> None: - tx_sum = self._daemon.get_miner_tx_sum(0, min(5000, self._daemon.get_height())) + def test_get_miner_tx_sum(self, daemon: MoneroDaemonRpc) -> None: + tx_sum = daemon.get_miner_tx_sum(0, min(5000, daemon.get_height())) DaemonUtils.test_miner_tx_sum(tx_sum) # Can get fee estimate @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_fee_estimate(self) -> None: - fee_estimate = self._daemon.get_fee_estimate() + def test_get_fee_estimate(self, daemon: MoneroDaemonRpc) -> None: + fee_estimate = daemon.get_fee_estimate() GenUtils.test_unsigned_big_integer(fee_estimate.fee, True) assert len(fee_estimate.fees) == 4, "Exptected 4 fees" for fee in fee_estimate.fees: @@ -411,89 +417,89 @@ def test_get_fee_estimate(self) -> None: # Can get general information @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_general_information(self): - info: MoneroDaemonInfo = self._daemon.get_info() + def test_get_general_information(self, daemon: MoneroDaemonRpc): + info: MoneroDaemonInfo = daemon.get_info() DaemonUtils.test_info(info) # Can get sync information @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_sync_information(self): - sync_info: MoneroDaemonSyncInfo = self._daemon.get_sync_info() + def test_get_sync_information(self, daemon: MoneroDaemonRpc): + sync_info: MoneroDaemonSyncInfo = daemon.get_sync_info() DaemonUtils.test_sync_info(sync_info) # Can get hard fork information @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_hard_fork_information(self): - hard_fork_info: MoneroHardForkInfo = self._daemon.get_hard_fork_info() + def test_get_hard_fork_information(self, daemon: MoneroDaemonRpc): + hard_fork_info: MoneroHardForkInfo = daemon.get_hard_fork_info() DaemonUtils.test_hard_fork_info(hard_fork_info) # Can get alternative chains @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_alternative_chains(self): - alt_chains: list[MoneroAltChain] = self._daemon.get_alt_chains() + def test_get_alternative_chains(self, daemon: MoneroDaemonRpc): + alt_chains: list[MoneroAltChain] = daemon.get_alt_chains() for altChain in alt_chains: DaemonUtils.test_alt_chain(altChain) # Can get alternative block hashes @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_alternative_block_ids(self): - alt_block_ids: list[str] = self._daemon.get_alt_block_hashes() + def test_get_alternative_block_ids(self, daemon: MoneroDaemonRpc): + alt_block_ids: list[str] = daemon.get_alt_block_hashes() for altBlockId in alt_block_ids: AssertUtils.assert_not_none(altBlockId) AssertUtils.assert_equals(64, len(altBlockId)) # TODO: common validation # Can get, set, and reset a download bandwidth limit @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_set_download_bandwidth(self): - init_val: int = self._daemon.get_download_limit() + def test_set_download_bandwidth(self, daemon: MoneroDaemonRpc): + init_val: int = daemon.get_download_limit() AssertUtils.assert_true(init_val > 0) set_val: int = init_val * 2 - self._daemon.set_download_limit(set_val) - AssertUtils.assert_equals(set_val, self._daemon.get_download_limit()) - reset_val: int = self._daemon.reset_download_limit() + daemon.set_download_limit(set_val) + AssertUtils.assert_equals(set_val, daemon.get_download_limit()) + reset_val: int = daemon.reset_download_limit() AssertUtils.assert_equals(init_val, reset_val) # test invalid limits try: - self._daemon.set_download_limit(0) + daemon.set_download_limit(0) raise Exception("Should have thrown error on invalid input") except Exception as e: AssertUtils.assert_equals("Download limit must be an integer greater than 0", str(e)) - AssertUtils.assert_equals(self._daemon.get_download_limit(), init_val) + AssertUtils.assert_equals(daemon.get_download_limit(), init_val) # Can get, set, and reset an upload bandwidth limit @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_set_upload_bandwidth(self): - init_val: int = self._daemon.get_upload_limit() + def test_set_upload_bandwidth(self, daemon: MoneroDaemonRpc): + init_val: int = daemon.get_upload_limit() AssertUtils.assert_true(init_val > 0) set_val: int = init_val * 2 - self._daemon.set_upload_limit(set_val) - AssertUtils.assert_equals(set_val, self._daemon.get_upload_limit()) - reset_val: int = self._daemon.reset_upload_limit() + daemon.set_upload_limit(set_val) + AssertUtils.assert_equals(set_val, daemon.get_upload_limit()) + reset_val: int = daemon.reset_upload_limit() AssertUtils.assert_equals(init_val, reset_val) # test invalid limits try: - self._daemon.set_upload_limit(0) + daemon.set_upload_limit(0) raise Exception("Should have thrown error on invalid input") except Exception as e: AssertUtils.assert_equals("Upload limit must be an integer greater than 0", str(e)) - AssertUtils.assert_equals(init_val, self._daemon.get_upload_limit()) + AssertUtils.assert_equals(init_val, daemon.get_upload_limit()) # Can get peers with active incoming or outgoing connections @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_peers(self): - peers: list[MoneroPeer] = self._daemon.get_peers() + def test_get_peers(self, daemon: MoneroDaemonRpc): + peers: list[MoneroPeer] = daemon.get_peers() AssertUtils.assert_false(len(peers) == 0, "Daemon has no incoming or outgoing peers to test") for peer in peers: DaemonUtils.test_peer(peer) # Can get all known peers which may be online or offline @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_known_peers(self): - peers: list[MoneroPeer] = self._daemon.get_known_peers() + def test_get_known_peers(self, daemon: MoneroDaemonRpc): + peers: list[MoneroPeer] = daemon.get_known_peers() if Utils.REGTEST: AssertUtils.assert_true(len(peers) == 0, "Regtest daemon should not have known peers to test") else: @@ -504,62 +510,60 @@ def test_get_known_peers(self): # Can limit the number of outgoing peers @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_set_outgoing_peer_limit(self): - self._daemon.set_outgoing_peer_limit(0) - self._daemon.set_outgoing_peer_limit(8) - self._daemon.set_outgoing_peer_limit(10) + def test_set_outgoing_peer_limit(self, daemon: MoneroDaemonRpc): + daemon.set_outgoing_peer_limit(0) + daemon.set_outgoing_peer_limit(8) + daemon.set_outgoing_peer_limit(10) # Can limit the number of incoming peers @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_set_incoming_peer_limit(self): - self._daemon.set_incoming_peer_limit(0) - self._daemon.set_incoming_peer_limit(8) - self._daemon.set_incoming_peer_limit(10) + def test_set_incoming_peer_limit(self, daemon: MoneroDaemonRpc): + daemon.set_incoming_peer_limit(0) + daemon.set_incoming_peer_limit(8) + daemon.set_incoming_peer_limit(10) # Can notify listeners when a new block is added to the chain @pytest.mark.skipif(Utils.LITE_MODE is True or Utils.TEST_NOTIFICATIONS is False, reason="TEST_NOTIFICATIONS disabled") - def test_block_listener(self): + def test_block_listener(self, daemon: MoneroDaemonRpc, wallet: MoneroWalletRpc): try: # start mining if possible to help push the network along - address: str = self._wallet.get_primary_address() + address: str = wallet.get_primary_address() try: - self._daemon.start_mining(address, 8, False, True) + daemon.start_mining(address, 8, False, True) except Exception as e: logger.warning(f"[!]: {str(e)}") # register a listener listener: MoneroDaemonListener = MoneroDaemonListener() - self._daemon.add_listener(listener) + daemon.add_listener(listener) # wait for next block notification - header: MoneroBlockHeader = self._daemon.wait_for_next_block_header() - self._daemon.remove_listener(listener) # unregister listener so daemon does not keep polling + header: MoneroBlockHeader = daemon.wait_for_next_block_header() + daemon.remove_listener(listener) # unregister listener so daemon does not keep polling BlockUtils.test_block_header(header, True) # test that listener was called with equivalent header AssertUtils.assert_equals(header, listener.last_header) - except Exception as e: - raise e finally: # stop mining try : - self._daemon.stop_mining() + daemon.stop_mining() except Exception as e: logger.warning(f"[!]: {str(e)}") # Can ban a peer @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_ban_peer(self): + def test_ban_peer(self, daemon: MoneroDaemonRpc): # set ban host = "192.168.1.51" ban = MoneroBan() ban.host = host ban.is_banned = True ban.seconds = 60 - self._daemon.set_peer_ban(ban) + daemon.set_peer_ban(ban) # test ban - bans = self._daemon.get_peer_bans() + bans = daemon.get_peer_bans() found: bool = False for peer_ban in bans: DaemonUtils.test_ban(peer_ban) @@ -570,7 +574,7 @@ def test_ban_peer(self): # Can ban peers @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_ban_peers(self): + def test_ban_peers(self, daemon: MoneroDaemonRpc): # set bans addr1 = "192.168.1.52" addr2 = "192.168.1.53" @@ -585,10 +589,10 @@ def test_ban_peers(self): bans: list[MoneroBan] = [] bans.append(ban1) bans.append(ban2) - self._daemon.set_peer_bans(bans) + daemon.set_peer_bans(bans) # test bans - bans = self._daemon.get_peer_bans() + bans = daemon.get_peer_bans() found1: bool = False found2: bool = False for aBan in bans: @@ -603,34 +607,34 @@ def test_ban_peers(self): # Can start and stop mining @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_mining(self): + def test_mining(self, daemon: MoneroDaemonRpc, wallet: MoneroWalletRpc): # stop mining at beginning of test try: - self._daemon.stop_mining() + daemon.stop_mining() except Exception as e: logger.warning(f"[!]: {str(e)}") # generate address to mine to - address: str = self._wallet.get_primary_address() + address: str = wallet.get_primary_address() # start mining - self._daemon.start_mining(address, 2, False, True) + daemon.start_mining(address, 2, False, True) # stop mining - self._daemon.stop_mining() + daemon.stop_mining() # Can get mining status @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_mining_status(self): + def test_get_mining_status(self, daemon: MoneroDaemonRpc, wallet: MoneroWalletRpc): try: # stop mining at beginning of test try: - self._daemon.stop_mining() + daemon.stop_mining() except Exception as e: logger.warning(f"[!]: {str(e)}") # test status without mining - status: MoneroMiningStatus = self._daemon.get_mining_status() + status: MoneroMiningStatus = daemon.get_mining_status() AssertUtils.assert_equals(False, status.is_active) AssertUtils.assert_is_none(status.address, f"Mining address is not None: {status.address}") AssertUtils.assert_equals(0, status.speed) @@ -638,39 +642,37 @@ def test_get_mining_status(self): AssertUtils.assert_is_none(status.is_background) # test status with mining - address: str = self._wallet.get_primary_address() + address: str = wallet.get_primary_address() thread_count: int = 3 is_background: bool = False - self._daemon.start_mining(address, thread_count, is_background, True) - status = self._daemon.get_mining_status() + daemon.start_mining(address, thread_count, is_background, True) + status = daemon.get_mining_status() assert status.speed is not None AssertUtils.assert_equals(True, status.is_active) AssertUtils.assert_equals(address, status.address) AssertUtils.assert_true(status.speed >= 0) AssertUtils.assert_equals(thread_count, status.num_threads) AssertUtils.assert_equals(is_background, status.is_background) - except Exception as e: - raise e finally: # stop mining at end of test try: - self._daemon.stop_mining() + daemon.stop_mining() except Exception as e: logger.warning(f"[!]: {str(e)}") # Can submit a mined block to the network @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @pytest.mark.flaky(reruns=5, reruns_delay=5) - def test_submit_mined_block(self): + def test_submit_mined_block(self, daemon: MoneroDaemonRpc): # get template to mine on - template: MoneroBlockTemplate = self._daemon.get_block_template(Utils.ADDRESS) + template: MoneroBlockTemplate = daemon.get_block_template(Utils.ADDRESS) assert template.block_template_blob is not None # TODO monero rpc: way to get mining nonce when found in order to submit? # try to submit block hashing blob without nonce try: - self._daemon.submit_block(template.block_template_blob) + daemon.submit_block(template.block_template_blob) raise Exception("Should have thrown error") except Exception as e: # AssertUtils.assert_equals(-7, (int) e.getCode()) @@ -678,8 +680,8 @@ def test_submit_mined_block(self): # Can prune the blockchain @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_prune_blockchain(self): - result: MoneroPruneResult = self._daemon.prune_blockchain(True) + def test_prune_blockchain(self, daemon: MoneroDaemonRpc): + result: MoneroPruneResult = daemon.prune_blockchain(True) if result.is_pruned: assert result.pruning_seed is not None @@ -690,27 +692,27 @@ def test_prune_blockchain(self): # Can check for an update @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @pytest.mark.flaky(reruns=5, reruns_delay=5) - def test_check_for_update(self): - result: MoneroDaemonUpdateCheckResult = self._daemon.check_for_update() + def test_check_for_update(self, daemon: MoneroDaemonRpc): + result: MoneroDaemonUpdateCheckResult = daemon.check_for_update() DaemonUtils.test_update_check_result(result) # Can download an update @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @pytest.mark.flaky(reruns=5, reruns_delay=5) - def test_download_update(self): + def test_download_update(self, daemon: MoneroDaemonRpc): # download to default path - result: MoneroDaemonUpdateDownloadResult = self._daemon.download_update() + result: MoneroDaemonUpdateDownloadResult = daemon.download_update() DaemonUtils.test_update_download_result(result, None) # download to defined path path: str = "test_download_" + str(time.time()) + ".tar.bz2" - result = self._daemon.download_update(path) + result = daemon.download_update(path) DaemonUtils.test_update_download_result(result, path) # test invalid path if result.is_update_available: try: - self._daemon.download_update("./ohhai/there") + daemon.download_update("./ohhai/there") raise Exception("Should have thrown error") except Exception as e: AssertUtils.assert_not_equals(str(e), "Should have thrown error") @@ -719,16 +721,16 @@ def test_download_update(self): # Can be stopped #@pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @pytest.mark.skip(reason="test is disabled to not interfere with other tests") - def test_stop(self): + def test_stop(self, daemon: MoneroDaemonRpc): # stop the daemon - self._daemon.stop() + daemon.stop() # give the daemon time to shut down # TimeUnit.MILLISECONDS.sleep(Utils.SYNC_PERIOD_IN_MS) # try to interact with the daemon try: - self._daemon.get_height() + daemon.get_height() raise Exception("Should have thrown error") except Exception as e: AssertUtils.assert_not_equals("Should have thrown error", str(e)) diff --git a/tests/test_monero_rpc_connection.py b/tests/test_monero_rpc_connection.py index be9b89d..bb36458 100644 --- a/tests/test_monero_rpc_connection.py +++ b/tests/test_monero_rpc_connection.py @@ -2,13 +2,14 @@ import logging from monero import MoneroRpcConnection -from utils import TestUtils as Utils, OsUtils, DaemonUtils +from utils import TestUtils as Utils, DaemonUtils logger: logging.Logger = logging.getLogger("TestMoneroRpcConnection") -@pytest.mark.skipif(OsUtils.is_windows(), reason="TODO setup test environment for windows") +@pytest.mark.integration class TestMoneroRpcConnection: + """Rpc connection integration tests""" @pytest.fixture(autouse=True) def setup_and_teardown(self, request: pytest.FixtureRequest): diff --git a/tests/test_monero_utils.py b/tests/test_monero_utils.py index a69f9ff..99fa1f1 100644 --- a/tests/test_monero_utils.py +++ b/tests/test_monero_utils.py @@ -13,7 +13,9 @@ logger: logging.Logger = logging.getLogger("TestMoneroUtils") +@pytest.mark.unit class TestMoneroUtils: + """Monero utilities unit tests""" class Config: mainnet: AddressBook = AddressBook() diff --git a/tests/test_monero_wallet_common.py b/tests/test_monero_wallet_common.py index 908e684..b2aec1c 100644 --- a/tests/test_monero_wallet_common.py +++ b/tests/test_monero_wallet_common.py @@ -1,6 +1,8 @@ from __future__ import annotations + import pytest import logging + from configparser import ConfigParser from abc import ABC, abstractmethod from typing import Optional @@ -9,12 +11,11 @@ from monero import ( MoneroWallet, MoneroWalletRpc, MoneroDaemonRpc, MoneroWalletConfig, MoneroTxConfig, MoneroDestination, MoneroRpcConnection, MoneroError, - MoneroKeyImage, MoneroTxQuery, MoneroUtils, MoneroWalletFull, - MoneroBlock + MoneroKeyImage, MoneroTxQuery, MoneroUtils, MoneroBlock ) from utils import ( TestUtils, WalletEqualityUtils, MiningUtils, - OsUtils, StringUtils, AssertUtils, TxUtils, + StringUtils, AssertUtils, TxUtils, TxContext, GenUtils, WalletUtils ) @@ -22,9 +23,9 @@ class BaseTestMoneroWallet(ABC): + """Common wallet integration tests""" CREATED_WALLET_KEYS_ERROR: str = "Wallet created from keys is not connected to authenticated daemon" - _wallet: MoneroWallet - _daemon: MoneroDaemonRpc + _funded: bool = False class Config: @@ -100,9 +101,18 @@ def test_config(self) -> BaseTestMoneroWallet.Config: @pytest.fixture(scope="class", autouse=True) def before_all(self): - if not OsUtils.is_windows() and not MiningUtils.blockchain_is_ready(): - MiningUtils.wait_until_blockchain_ready() - self.fund_test_wallet() + MiningUtils.wait_until_blockchain_ready() + self.fund_test_wallet() + + @pytest.fixture(scope="class") + def daemon(self) -> MoneroDaemonRpc: + """Test rpc daemon instance""" + return TestUtils.get_daemon_rpc() + + @pytest.fixture(scope="class") + def wallet(self) -> MoneroWallet: + """Test wallet instance""" + pytest.skip("No wallet test instance setup") @pytest.fixture(autouse=True) def setup_and_teardown(self, request: pytest.FixtureRequest): @@ -122,18 +132,15 @@ def after_each(self, request: pytest.FixtureRequest): # Can get the daemon's max peer height @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_daemon_max_peer_height(self) -> None: - height: int = 0 - if isinstance(self._wallet, MoneroWalletFull): - height = self._wallet.get_daemon_max_peer_height() - + def test_get_daemon_max_peer_height(self, wallet: MoneroWallet) -> None: + height = wallet.get_daemon_max_peer_height() assert height > 0 # Can get the daemon's height @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_daemon(self) -> None: - assert self._wallet.is_connected_to_daemon(), "Wallet is not connected to daemon" - daemon_height = self._wallet.get_daemon_height() + def test_daemon(self, wallet: MoneroWallet) -> None: + assert wallet.is_connected_to_daemon(), "Wallet is not connected to daemon" + daemon_height = wallet.get_daemon_height() assert daemon_height > 0 @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @@ -144,7 +151,6 @@ def test_create_wallet_random(self) -> None: config = MoneroWalletConfig() wallet = self._create_wallet(config) path = wallet.get_path() - e2: Exception | None = None try: MoneroUtils.validate_address(wallet.get_primary_address(), TestUtils.NETWORK_TYPE) @@ -154,12 +160,8 @@ def test_create_wallet_random(self) -> None: if not isinstance(wallet, MoneroWalletRpc): # TODO monero-wallet-rpc: get seed language AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) - except Exception as e: - e2 = e - - self._close_wallet(wallet) - if e2 is not None: - raise e2 + finally: + self._close_wallet(wallet) # attempt to create wallet at same path try: @@ -179,33 +181,28 @@ def test_create_wallet_random(self) -> None: AssertUtils.assert_equals("Unknown language: english", str(e)) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_create_wallet_from_seed(self, test_config: BaseTestMoneroWallet.Config) -> None: + def test_create_wallet_from_seed(self, wallet: MoneroWallet, test_config: BaseTestMoneroWallet.Config) -> None: # save for comparison - primary_address = self._wallet.get_primary_address() - private_view_key = self._wallet.get_private_view_key() - private_spend_key = self._wallet.get_private_spend_key() + primary_address = wallet.get_primary_address() + private_view_key = wallet.get_private_view_key() + private_spend_key = wallet.get_private_spend_key() # recreate test wallet from seed config = MoneroWalletConfig() config.seed = TestUtils.SEED config.restore_height = TestUtils.FIRST_RECEIVE_HEIGHT - wallet: MoneroWallet = self._create_wallet(config) - path = wallet.get_path() - e2: Exception | None = None + w: MoneroWallet = self._create_wallet(config) + path = w.get_path() try: - AssertUtils.assert_equals(primary_address, wallet.get_primary_address()) - AssertUtils.assert_equals(private_view_key, wallet.get_private_view_key()) - AssertUtils.assert_equals(private_spend_key, wallet.get_private_spend_key()) - AssertUtils.assert_equals(TestUtils.SEED, wallet.get_seed()) - if not isinstance(wallet, MoneroWalletRpc): - AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) - except Exception as e: - e2 = e - - self._close_wallet(wallet) - if e2 is not None: - raise e2 + AssertUtils.assert_equals(primary_address, w.get_primary_address()) + AssertUtils.assert_equals(private_view_key, w.get_private_view_key()) + AssertUtils.assert_equals(private_spend_key, w.get_private_spend_key()) + AssertUtils.assert_equals(TestUtils.SEED, w.get_seed()) + if not isinstance(w, MoneroWalletRpc): + AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, w.get_seed_language()) + finally: + self._close_wallet(w) # attempt to create wallet with two missing words try: @@ -227,98 +224,75 @@ def test_create_wallet_from_seed(self, test_config: BaseTestMoneroWallet.Config) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") def test_create_wallet_from_seed_with_offset(self) -> None: - e1: Exception | None = None + # create test wallet with offset + config = MoneroWalletConfig() + config.seed = TestUtils.SEED + config.restore_height = TestUtils.FIRST_RECEIVE_HEIGHT + config.seed_offset = "my secret offset!" + wallet: MoneroWallet = self._create_wallet(config) try: - - # create test wallet with offset - config = MoneroWalletConfig() - config.seed = TestUtils.SEED - config.restore_height = TestUtils.FIRST_RECEIVE_HEIGHT - config.seed_offset = "my secret offset!" - wallet: MoneroWallet = self._create_wallet(config) - e2: Exception | None = None - try: - MoneroUtils.validate_mnemonic(wallet.get_seed()) - AssertUtils.assert_not_equals(TestUtils.SEED, wallet.get_seed()) - MoneroUtils.validate_address(wallet.get_primary_address(), TestUtils.NETWORK_TYPE) - AssertUtils.assert_not_equals(TestUtils.ADDRESS, wallet.get_primary_address()) - if not isinstance(wallet, MoneroWalletRpc): - # TODO monero-wallet-rpc: support - AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) - except Exception as e: - e2 = e - + MoneroUtils.validate_mnemonic(wallet.get_seed()) + AssertUtils.assert_not_equals(TestUtils.SEED, wallet.get_seed()) + MoneroUtils.validate_address(wallet.get_primary_address(), TestUtils.NETWORK_TYPE) + AssertUtils.assert_not_equals(TestUtils.ADDRESS, wallet.get_primary_address()) + if not isinstance(wallet, MoneroWalletRpc): + # TODO monero-wallet-rpc: support + AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) + finally: self._close_wallet(wallet) - if e2 is not None: - raise e2 - except Exception as e: - e1 = e - - if e1 is not None: - raise e1 @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_create_wallet_from_keys(self) -> None: + def test_create_wallet_from_keys(self, daemon: MoneroDaemonRpc, wallet: MoneroWallet) -> None: # save for comparison - primary_address = self._wallet.get_primary_address() - private_view_key = self._wallet.get_private_view_key() - private_spend_key = self._wallet.get_private_spend_key() + primary_address = wallet.get_primary_address() + private_view_key = wallet.get_private_view_key() + private_spend_key = wallet.get_private_spend_key() # recreate test wallet from keys config = MoneroWalletConfig() config.primary_address = primary_address config.private_view_key = private_view_key config.private_spend_key = private_spend_key - config.restore_height = self._daemon.get_height() - wallet: MoneroWallet = self._create_wallet(config) - path = wallet.get_path() - e2: Exception | None = None + config.restore_height = daemon.get_height() + w: MoneroWallet = self._create_wallet(config) + path = w.get_path() + try: - AssertUtils.assert_equals(primary_address, wallet.get_primary_address()) - AssertUtils.assert_equals(private_view_key, wallet.get_private_view_key()) - AssertUtils.assert_equals(private_spend_key, wallet.get_private_spend_key()) - if not wallet.is_connected_to_daemon(): + AssertUtils.assert_equals(primary_address, w.get_primary_address()) + AssertUtils.assert_equals(private_view_key, w.get_private_view_key()) + AssertUtils.assert_equals(private_spend_key, w.get_private_spend_key()) + if not w.is_connected_to_daemon(): # TODO monero-project: keys wallets not connected logger.warning(f"WARNING: {self.CREATED_WALLET_KEYS_ERROR}") - AssertUtils.assert_true(wallet.is_connected_to_daemon(), self.CREATED_WALLET_KEYS_ERROR) - if not isinstance(wallet, MoneroWalletRpc): + AssertUtils.assert_true(w.is_connected_to_daemon(), self.CREATED_WALLET_KEYS_ERROR) + if not isinstance(w, MoneroWalletRpc): # TODO monero-wallet-rpc: cannot get seed from wallet created from keys? - MoneroUtils.validate_mnemonic(wallet.get_seed()) - AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) - - except Exception as e: - e2 = e - - self._close_wallet(wallet) - if e2 is not None: - raise e2 + MoneroUtils.validate_mnemonic(w.get_seed()) + AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, w.get_seed_language()) + finally: + self._close_wallet(w) # recreate test wallet from spend key - if not isinstance(wallet, MoneroWalletRpc): # TODO monero-wallet-rpc: cannot create wallet from spend key? + if not isinstance(w, MoneroWalletRpc): # TODO monero-wallet-rpc: cannot create wallet from spend key? config = MoneroWalletConfig() config.private_spend_key = private_spend_key - config.restore_height = self._daemon.get_height() - wallet = self._create_wallet(config) - e2 = None + config.restore_height = daemon.get_height() + w = self._create_wallet(config) + try: - AssertUtils.assert_equals(primary_address, wallet.get_primary_address()) - AssertUtils.assert_equals(private_view_key, wallet.get_private_view_key()) - AssertUtils.assert_equals(private_spend_key, wallet.get_private_spend_key()) - if not wallet.is_connected_to_daemon(): + AssertUtils.assert_equals(primary_address, w.get_primary_address()) + AssertUtils.assert_equals(private_view_key, w.get_private_view_key()) + AssertUtils.assert_equals(private_spend_key, w.get_private_spend_key()) + if not w.is_connected_to_daemon(): # TODO monero-project: keys wallets not connected logger.warning(f"{self.CREATED_WALLET_KEYS_ERROR}") - AssertUtils.assert_true(wallet.is_connected_to_daemon(), self.CREATED_WALLET_KEYS_ERROR) - if not isinstance(wallet, MoneroWalletRpc): + AssertUtils.assert_true(w.is_connected_to_daemon(), self.CREATED_WALLET_KEYS_ERROR) + if not isinstance(w, MoneroWalletRpc): # TODO monero-wallet-rpc: cannot get seed from wallet created from keys? - MoneroUtils.validate_mnemonic(wallet.get_seed()) - AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) - - except Exception as e: - e2 = e - - self._close_wallet(wallet) - if e2 is not None: - raise e2 + MoneroUtils.validate_mnemonic(w.get_seed()) + AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, w.get_seed_language()) + finally: + self._close_wallet(w) # attempt to create wallet at same path try: @@ -330,8 +304,7 @@ def test_create_wallet_from_keys(self) -> None: AssertUtils.assert_equals("Wallet already exists: " + path, str(e)) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_subaddress_lookahead(self) -> None: - e1: Exception | None = None + def test_subaddress_lookahead(self, wallet: MoneroWallet) -> None: receiver: MoneroWallet | None = None try: # create wallet with high subaddress lookahead @@ -349,23 +322,19 @@ def test_subaddress_lookahead(self) -> None: tx_config.destinations.append(dest) tx_config.relay = True - self._wallet.create_tx(tx_config) + wallet.create_tx(tx_config) # observe unconfirmed funds GenUtils.wait_for(1000) receiver.sync() assert receiver.get_balance() > 0 - except Exception as e: - e1 = e - - if receiver is not None: - self._close_wallet(receiver) - if e1 is not None: - raise e1 + finally: + if receiver is not None: + self._close_wallet(receiver) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_version(self) -> None: - version = self._wallet.get_version() + def test_get_version(self, wallet: MoneroWallet) -> None: + version = wallet.get_version() assert version.number is not None assert version.number > 0 assert version.is_release is not None @@ -464,14 +433,14 @@ def test_set_daemon_connection(self): self._close_wallet(wallet) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_seed(self): - seed = self._wallet.get_seed() + def test_get_seed(self, wallet: MoneroWallet): + seed = wallet.get_seed() MoneroUtils.validate_mnemonic(seed) AssertUtils.assert_equals(TestUtils.SEED, seed) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_seed_language(self): - language = self._wallet.get_seed_language() + def test_get_seed_language(self, wallet: MoneroWallet): + language = wallet.get_seed_language() AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, language) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @@ -482,52 +451,51 @@ def test_get_seed_languages(self): assert len(language) > 0 @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_private_view_key(self): - private_view_key = self._wallet.get_private_view_key() + def test_get_private_view_key(self, wallet: MoneroWallet): + private_view_key = wallet.get_private_view_key() MoneroUtils.validate_private_view_key(private_view_key) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_private_spend_key(self): - private_spend_key = self._wallet.get_private_spend_key() + def test_get_private_spend_key(self, wallet: MoneroWallet): + private_spend_key = wallet.get_private_spend_key() MoneroUtils.validate_private_spend_key(private_spend_key) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_public_view_key(self): - public_view_key = self._wallet.get_public_view_key() + def test_get_public_view_key(self, wallet: MoneroWallet): + public_view_key = wallet.get_public_view_key() MoneroUtils.validate_private_spend_key(public_view_key) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_public_spend_key(self): - public_spend_key = self._wallet.get_public_spend_key() + def test_get_public_spend_key(self, wallet: MoneroWallet): + public_spend_key = wallet.get_public_spend_key() MoneroUtils.validate_private_spend_key(public_spend_key) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_primary_address(self): - primary_address = self._wallet.get_primary_address() + def test_get_primary_address(self, wallet: MoneroWallet): + primary_address = wallet.get_primary_address() MoneroUtils.validate_address(primary_address, TestUtils.NETWORK_TYPE) - AssertUtils.assert_equals(self._wallet.get_address(0, 0), primary_address) + AssertUtils.assert_equals(wallet.get_address(0, 0), primary_address) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_subaddress_address(self): - AssertUtils.assert_equals(self._wallet.get_primary_address(), (self._wallet.get_address(0, 0))) - for account in self._wallet.get_accounts(True): + def test_get_subaddress_address(self, wallet: MoneroWallet): + AssertUtils.assert_equals(wallet.get_primary_address(), (wallet.get_address(0, 0))) + for account in wallet.get_accounts(True): for subaddress in account.subaddresses: assert account.index is not None assert subaddress.index is not None - AssertUtils.assert_equals(subaddress.address, self._wallet.get_address(account.index, subaddress.index)) + AssertUtils.assert_equals(subaddress.address, wallet.get_address(account.index, subaddress.index)) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_subaddress_address_out_of_range(self): - accounts = self._wallet.get_accounts(True) + def test_get_subaddress_address_out_of_range(self, wallet: MoneroWallet): + accounts = wallet.get_accounts(True) account_idx = len(accounts) - 1 subaddress_idx = len(accounts[account_idx].subaddresses) - address = self._wallet.get_address(account_idx, subaddress_idx) + address = wallet.get_address(account_idx, subaddress_idx) AssertUtils.assert_not_none(address) AssertUtils.assert_true(len(address) > 0) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_address_indices(self): - wallet = self._wallet + def test_get_address_indices(self, wallet: MoneroWallet): # get last subaddress to test accounts = wallet.get_accounts(True) account_idx = len(accounts) - 1 @@ -556,8 +524,7 @@ def test_get_address_indices(self): AssertUtils.assert_equals("Invalid address", str(e)) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_decode_integrated_address(self): - wallet = self._wallet + def test_decode_integrated_address(self, wallet: MoneroWallet): integrated_address = wallet.get_integrated_address('', "03284e41c342f036") decoded_address = wallet.decode_integrated_address(integrated_address.integrated_address) AssertUtils.assert_equals(integrated_address, decoded_address) @@ -570,9 +537,7 @@ def test_decode_integrated_address(self): AssertUtils.assert_equals("Invalid address", str(e)) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_sync_without_progress(self): - wallet = self._wallet - daemon = self._daemon + def test_sync_without_progress(self, daemon: MoneroDaemonRpc, wallet: MoneroWallet): num_blocks = 100 chain_height = daemon.get_height() AssertUtils.assert_true(chain_height >= num_blocks) @@ -581,9 +546,7 @@ def test_sync_without_progress(self): AssertUtils.assert_not_none(result.received_money) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_wallet_equality_ground_truth(self): - wallet = self._wallet - daemon = self._daemon + def test_wallet_equality_ground_truth(self, daemon: MoneroDaemonRpc, wallet: MoneroWallet): TestUtils.WALLET_TX_TRACKER.wait_for_wallet_txs_to_clear_pool(daemon, TestUtils.SYNC_PERIOD_IN_MS, [wallet]) wallet_gt = TestUtils.create_wallet_ground_truth( TestUtils.NETWORK_TYPE, TestUtils.SEED, None, TestUtils.FIRST_RECEIVE_HEIGHT @@ -596,12 +559,12 @@ def test_wallet_equality_ground_truth(self): wallet_gt.close() @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_height(self): - height = self._wallet.get_height() + def test_get_height(self, wallet: MoneroWallet): + height = wallet.get_height() assert height >= 0 @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_height_by_date(self): + def test_get_height_by_date(self, wallet: MoneroWallet): # collect dates to test starting 100 days ago day_ms = 24 * 60 * 60 * 1000 # TODO monero-project: today's date can throw exception as "in future" so we test up to yesterday @@ -616,7 +579,7 @@ def test_get_height_by_date(self): # test heights by date last_height: Optional[int] = None for date in dates: - height = self._wallet.get_height_by_date(date.year + 1900, date.month + 1, date.day) + height = wallet.get_height_by_date(date.year + 1900, date.month + 1, date.day) assert (height >= 0) if last_height is not None: assert (height >= last_height) @@ -624,22 +587,21 @@ def test_get_height_by_date(self): assert last_height is not None assert (last_height >= 0) - height = self._wallet.get_height() + height = wallet.get_height() assert (height >= 0) # test future date try: tomorrow = datetime.fromtimestamp((yesterday + day_ms * 2) / 1000) - self._wallet.get_height_by_date(tomorrow.year + 1900, tomorrow.month + 1, tomorrow.day) + wallet.get_height_by_date(tomorrow.year + 1900, tomorrow.month + 1, tomorrow.day) raise Exception("Expected exception on future date") except MoneroError as err: assert "specified date is in the future" == str(err) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_all_balances(self): + def test_get_all_balances(self, wallet: MoneroWallet): # fetch accounts with all info as reference - accounts = self._wallet.get_accounts(True) - wallet = self._wallet + accounts = wallet.get_accounts(True) # test that balances add up between accounts and wallet accounts_balance = 0 accounts_unlocked_balance = 0 @@ -675,47 +637,46 @@ def test_get_all_balances(self): assert wallet.get_unlocked_balance() == accounts_unlocked_balance @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_accounts_without_subaddresses(self): - accounts = self._wallet.get_accounts() + def test_get_accounts_without_subaddresses(self, wallet: MoneroWallet): + accounts = wallet.get_accounts() assert len(accounts) > 0 for account in accounts: WalletUtils.test_account(account, TestUtils.NETWORK_TYPE) assert len(account.subaddresses) == 0 @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_accounts_with_subaddresses(self): - accounts = self._wallet.get_accounts(True) + def test_get_accounts_with_subaddresses(self, wallet: MoneroWallet): + accounts = wallet.get_accounts(True) assert len(accounts) > 0 for account in accounts: WalletUtils.test_account(account, TestUtils.NETWORK_TYPE) assert len(account.subaddresses) > 0 @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_account(self): - accounts = self._wallet.get_accounts() + def test_get_account(self, wallet: MoneroWallet): + accounts = wallet.get_accounts() assert len(accounts) > 0 for account in accounts: WalletUtils.test_account(account, TestUtils.NETWORK_TYPE) # test without subaddresses assert account.index is not None - retrieved = self._wallet.get_account(account.index) + retrieved = wallet.get_account(account.index) assert len(retrieved.subaddresses) == 0 # test with subaddresses - retrieved = self._wallet.get_account(account.index, True) + retrieved = wallet.get_account(account.index, True) assert len(retrieved.subaddresses) > 0 @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_create_account_without_label(self): - accounts_before = self._wallet.get_accounts() - created_account = self._wallet.create_account() + def test_create_account_without_label(self, wallet: MoneroWallet): + accounts_before = wallet.get_accounts() + created_account = wallet.create_account() WalletUtils.test_account(created_account, TestUtils.NETWORK_TYPE) - assert len(accounts_before) == len(self._wallet.get_accounts()) - 1 + assert len(accounts_before) == len(wallet.get_accounts()) - 1 @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_create_account_with_label(self): - wallet = self._wallet + def test_create_account_with_label(self, wallet: MoneroWallet): # create account with label accounts_before = wallet.get_accounts() label = StringUtils.get_random_string() @@ -740,9 +701,8 @@ def test_create_account_with_label(self): created_account = wallet.get_account(created_account.index) WalletUtils.test_account(created_account, TestUtils.NETWORK_TYPE) - def test_set_account_label(self): + def test_set_account_label(self, wallet: MoneroWallet): # create account - wallet = self._wallet if len(wallet.get_accounts()) < 2: wallet.create_account() @@ -752,8 +712,7 @@ def test_set_account_label(self): assert label == wallet.get_subaddress(1, 0).label @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_subaddresses(self): - wallet = self._wallet + def test_get_subaddresses(self, wallet: MoneroWallet): accounts = wallet.get_accounts() assert len(accounts) > 0 for account in accounts: @@ -765,8 +724,7 @@ def test_get_subaddresses(self): assert account.index == subaddress.account_index @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_subaddresses_by_indices(self): - wallet = self._wallet + def test_get_subaddresses_by_indices(self, wallet: MoneroWallet): accounts = wallet.get_accounts() assert len(accounts) > 0 for account in accounts: @@ -796,8 +754,7 @@ def test_get_subaddresses_by_indices(self): AssertUtils.assert_subaddresses_equal(subaddresses, fetched_subaddresses) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_subaddress_by_index(self): - wallet = self._wallet + def test_get_subaddress_by_index(self, wallet: MoneroWallet): accounts = wallet.get_accounts() assert len(accounts) > 0 for account in accounts: @@ -814,8 +771,7 @@ def test_get_subaddress_by_index(self): ) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_create_subaddress(self): - wallet = self._wallet + def test_create_subaddress(self, wallet: MoneroWallet): # create subaddresses across accounts accounts = wallet.get_accounts() if len(accounts) < 2: @@ -846,8 +802,7 @@ def test_create_subaddress(self): account_idx += 1 - def test_set_subaddress_label(self): - wallet = self._wallet + def test_set_subaddress_label(self, wallet: MoneroWallet): # create subaddresses while len(wallet.get_subaddresses(0)) < 3: wallet.create_subaddress(0) @@ -923,8 +878,7 @@ def test_get_txs_wallet(self) -> None: #assert non_default_incoming, "No incoming transfers found to non-default account and subaddress; run send-to-multiple tests first" @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_set_tx_note(self) -> None: - wallet = self._wallet + def test_set_tx_note(self, wallet: MoneroWallet) -> None: txs = TxUtils.get_random_transactions(wallet, None, 1, 5) # set notes @@ -946,8 +900,7 @@ def test_set_tx_note(self) -> None: i += 1 @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_set_tx_notes(self): - wallet = self._wallet + def test_set_tx_notes(self, wallet: MoneroWallet): # set tx notes uuid = StringUtils.get_random_string() txs = wallet.get_txs() @@ -974,8 +927,7 @@ def test_set_tx_notes(self): #endregion @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_export_key_images(self): - wallet = self._wallet + def test_export_key_images(self, wallet: MoneroWallet): images = wallet.export_key_images(True) assert len(images) > 0, "No signed key images in wallet" @@ -990,8 +942,7 @@ def test_export_key_images(self): assert len(images_all) > len(images) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_new_key_images_from_last_import(self): - wallet = self._wallet + def test_get_new_key_images_from_last_import(self, wallet: MoneroWallet): # get outputs hex outputs_hex = wallet.export_outputs() @@ -1010,8 +961,7 @@ def test_get_new_key_images_from_last_import(self): assert image.signature is not None and len(image.signature) > 0 @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_import_key_images(self): - wallet = self._wallet + def test_import_key_images(self, wallet: MoneroWallet): images = wallet.export_key_images() assert len(images) > 0, "Wallet does not have any key images run send tests" result = wallet.import_key_images(images) @@ -1031,8 +981,7 @@ def test_import_key_images(self): GenUtils.test_unsigned_big_integer(result.unspent_amount, has_unspent) @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_get_payment_uri(self): - wallet = self._wallet + def test_get_payment_uri(self, wallet: MoneroWallet): # test with address and amount config1 = MoneroTxConfig() config1.address = wallet.get_address(0, 0) @@ -1073,10 +1022,7 @@ def test_get_payment_uri(self): assert str(e).index("Cannot make URI from supplied parameters") >= 0 @pytest.mark.skipif(TestUtils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_mining(self): - daemon = self._daemon - wallet = self._wallet - + def test_mining(self, daemon: MoneroDaemonRpc, wallet: MoneroWallet): status = daemon.get_mining_status() if status.is_active: wallet.stop_mining() diff --git a/tests/test_monero_wallet_full.py b/tests/test_monero_wallet_full.py index 35190be..9c3a196 100644 --- a/tests/test_monero_wallet_full.py +++ b/tests/test_monero_wallet_full.py @@ -9,7 +9,7 @@ ) from utils import ( - TestUtils as Utils, OsUtils, StringUtils, + TestUtils as Utils, StringUtils, AssertUtils, WalletUtils ) from test_monero_wallet_common import BaseTestMoneroWallet @@ -17,11 +17,9 @@ logger: logging.Logger = logging.getLogger("TestMoneroWalletFull") -@pytest.mark.skipif(OsUtils.is_windows(), reason="TODO setup test environment for windows") +@pytest.mark.integration class TestMoneroWalletFull(BaseTestMoneroWallet): - - _daemon: MoneroDaemonRpc = Utils.get_daemon_rpc() - _wallet: MoneroWalletFull = Utils.get_wallet_full() # type: ignore + """Full wallet integration tests""" #region Overrides @@ -39,7 +37,7 @@ def _create_wallet(self, config: Optional[MoneroWalletConfig], start_syncing: bo config.network_type = Utils.NETWORK_TYPE #if config.server is None and config.connection_manager is None: if config.server is None: - config.server = self._daemon.get_rpc_connection() + config.server = Utils.get_daemon_rpc_connection() if config.restore_height is None and not random: config.restore_height = 0 @@ -63,7 +61,7 @@ def _open_wallet(self, config: Optional[MoneroWalletConfig], start_syncing: bool if config.network_type is None: config.network_type = Utils.NETWORK_TYPE if config.server is None and config.connection_manager is None: - config.server = self._daemon.get_rpc_connection() + config.server = Utils.get_daemon_rpc_connection() # open wallet assert config.network_type is not None @@ -80,10 +78,10 @@ def _close_wallet(self, wallet: MoneroWallet, save: bool = False) -> None: @override def _get_seed_languages(self) -> list[str]: - return self._wallet.get_seed_languages() + return self.get_test_wallet().get_seed_languages() @override - def get_test_wallet(self) -> MoneroWallet: + def get_test_wallet(self) -> MoneroWalletFull: return Utils.get_wallet_full() #endregion @@ -93,34 +91,34 @@ def get_test_wallet(self) -> MoneroWallet: # Can create a subaddress with and without a label @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @override - def test_create_subaddress(self): + def test_create_subaddress(self, wallet: MoneroWalletFull): # type: ignore # create subaddresses across accounts - accounts: list[MoneroAccount] = self._wallet.get_accounts() + accounts: list[MoneroAccount] = wallet.get_accounts() if len(accounts) < 2: - self._wallet.create_account() + wallet.create_account() - accounts = self._wallet.get_accounts() + accounts = wallet.get_accounts() AssertUtils.assert_true(len(accounts) > 1) account_idx: int = 0 while account_idx < 2: # create subaddress with no label - subaddresses: list[MoneroSubaddress] = self._wallet.get_subaddresses(account_idx) - subaddress: MoneroSubaddress = self._wallet.create_subaddress(account_idx) + subaddresses: list[MoneroSubaddress] = wallet.get_subaddresses(account_idx) + subaddress: MoneroSubaddress = wallet.create_subaddress(account_idx) # TODO fix monero-cpp/monero_wallet_full.cpp to return boost::none on empty label #assert subaddress.label is None assert subaddress.label is None or subaddress.label == "" WalletUtils.test_subaddress(subaddress) - subaddresses_new: list[MoneroSubaddress] = self._wallet.get_subaddresses(account_idx) + subaddresses_new: list[MoneroSubaddress] = wallet.get_subaddresses(account_idx) AssertUtils.assert_equals(len(subaddresses_new) - 1, len(subaddresses)) AssertUtils.assert_equals(subaddress, subaddresses_new[len(subaddresses_new) - 1]) # create subaddress with label - subaddresses = self._wallet.get_subaddresses(account_idx) + subaddresses = wallet.get_subaddresses(account_idx) uuid: str = StringUtils.get_random_string() - subaddress = self._wallet.create_subaddress(account_idx, uuid) + subaddress = wallet.create_subaddress(account_idx, uuid) AssertUtils.assert_equals(uuid, subaddress.label) WalletUtils.test_subaddress(subaddress) - subaddresses_new = self._wallet.get_subaddresses(account_idx) + subaddresses_new = wallet.get_subaddresses(account_idx) AssertUtils.assert_equals(len(subaddresses), len(subaddresses_new) - 1) AssertUtils.assert_equals(subaddress, subaddresses_new[len(subaddresses_new) - 1]) account_idx += 1 @@ -131,52 +129,52 @@ def test_create_subaddress(self): @pytest.mark.skipif(Utils.REGTEST, reason="Cannot retrieve accurate height by date from regtest fakechain") @override - def test_get_height_by_date(self): - return super().test_get_height_by_date() + def test_get_height_by_date(self, wallet: MoneroWallet): + return super().test_get_height_by_date(wallet) @pytest.mark.skip(reason="TODO") @override - def test_wallet_equality_ground_truth(self): - return super().test_wallet_equality_ground_truth() + def test_wallet_equality_ground_truth(self, daemon: MoneroDaemonRpc, wallet: MoneroWallet): + return super().test_wallet_equality_ground_truth(daemon, wallet) @pytest.mark.skip(reason="TODO fix MoneroTxConfig.serialize()") @override - def test_get_payment_uri(self): - return super().test_get_payment_uri() + def test_get_payment_uri(self, wallet: MoneroWallet): + return super().test_get_payment_uri(wallet) @pytest.mark.skip(reason="TODO") @override - def test_set_tx_note(self) -> None: - return super().test_set_tx_note() + def test_set_tx_note(self, wallet: MoneroWallet) -> None: + return super().test_set_tx_note(wallet) @pytest.mark.skip(reason="TODO") @override - def test_set_tx_notes(self): - return super().test_set_tx_notes() + def test_set_tx_notes(self, wallet: MoneroWallet): + return super().test_set_tx_notes(wallet) @pytest.mark.skip(reason="TODO") @override - def test_export_key_images(self): - return super().test_export_key_images() + def test_export_key_images(self, wallet: MoneroWallet): + return super().test_export_key_images(wallet) @pytest.mark.skip(reason="TODO (monero-project): https://github.com/monero-project/monero/issues/5812") @override - def test_import_key_images(self): - return super().test_import_key_images() + def test_import_key_images(self, wallet: MoneroWallet): + return super().test_import_key_images(wallet) @pytest.mark.skip(reason="TODO") @override - def test_get_new_key_images_from_last_import(self): - return super().test_get_new_key_images_from_last_import() + def test_get_new_key_images_from_last_import(self, wallet: MoneroWallet): + return super().test_get_new_key_images_from_last_import(wallet) @pytest.mark.skip(reason="TODO") @override - def test_subaddress_lookahead(self) -> None: - return super().test_subaddress_lookahead() + def test_subaddress_lookahead(self, wallet: MoneroWallet) -> None: + return super().test_subaddress_lookahead(wallet) @pytest.mark.skip(reason="TODO fix segmentation fault") @override - def test_set_account_label(self) -> None: - super().test_set_account_label() + def test_set_account_label(self, wallet: MoneroWallet) -> None: + super().test_set_account_label(wallet) #endregion diff --git a/tests/test_monero_wallet_interface.py b/tests/test_monero_wallet_interface.py index a5e7b0a..3ce14df 100644 --- a/tests/test_monero_wallet_interface.py +++ b/tests/test_monero_wallet_interface.py @@ -1,27 +1,25 @@ import pytest import logging -from typing import Optional from monero import ( MoneroWallet, MoneroConnectionManager, MoneroRpcConnection, MoneroWalletListener, MoneroTransferQuery, MoneroOutputQuery, - MoneroTxConfig, MoneroTxSet, MoneroMessageSignatureType + MoneroTxConfig, MoneroTxSet, MoneroMessageSignatureType, + MoneroTxWallet ) -from utils import AssertUtils logger: logging.Logger = logging.getLogger("TestMoneroWalletInterface") # Test calls to MoneroWallet interface +@pytest.mark.unit class TestMoneroWalletInterface: + """Wallet interface bindings unit tests""" - _wallet: Optional[MoneroWallet] = None - - def _get_wallet(self) -> MoneroWallet: - if self._wallet is None: - self._wallet = MoneroWallet() - - return self._wallet + @pytest.fixture(scope="class") + def wallet(self) -> MoneroWallet: + """Test wallet instance""" + return MoneroWallet() @pytest.fixture(autouse=True) def setup_and_teardown(self, request: pytest.FixtureRequest): @@ -31,694 +29,517 @@ def setup_and_teardown(self, request: pytest.FixtureRequest): #region Tests - # Test static default language - def test_defaults(self) -> None: + # Test default language static property + def test_default_language(self) -> None: assert MoneroWallet.DEFAULT_LANGUAGE is not None, "MoneroWallet.DEFAULT_LANGUAGE is None" assert MoneroWallet.DEFAULT_LANGUAGE == "English", f'Expected "English", got {MoneroWallet.DEFAULT_LANGUAGE}' - # Test wallet general info - def test_info(self) -> None: - wallet = self._get_wallet() - - try: - wallet.is_view_only() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_version() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_path() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_network_type() - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test wallet connection - def test_connection(self) -> None: - wallet = self._get_wallet() - - try: - wallet.set_connection_manager(MoneroConnectionManager()) - except Exception as e: - AssertUtils.assert_not_supported(e) - -# TODO segmentation fault -# try: -# wallet.get_connection_manager() -# except Exception as e: -# AssertUtils.assert_not_supported(e) - - try: - wallet.set_daemon_connection('') - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.set_daemon_connection(MoneroRpcConnection()) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_daemon_connection() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.is_connected_to_daemon() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.is_daemon_trusted() - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test get wallet keys - def test_keys(self) -> None: - wallet = self._get_wallet() - - try: - wallet.get_seed() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_seed_language() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_public_view_key() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_private_view_key() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_public_spend_key() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_private_spend_key() - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test wallet addresses - def test_addresses(self) -> None: - wallet = self._get_wallet() - - try: - wallet.get_primary_address() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_address(0, 0) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_address_index("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_integrated_address() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.decode_integrated_address("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test get wallet sync info - def test_sync_utils(self) -> None: - wallet = self._get_wallet() - - try: - wallet.is_synced() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_height() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_restore_height() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.set_restore_height(0) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_daemon_height() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_daemon_max_peer_height() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_height_by_date(0, 0, 0) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.add_listener(MoneroWalletListener()) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.remove_listener(MoneroWalletListener()) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_listeners() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.sync() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.sync(MoneroWalletListener()) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.sync(0) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.sync(0, MoneroWalletListener()) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.start_syncing() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.stop_syncing() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.scan_txs(["", "", ""]) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.rescan_spent() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.rescan_blockchain() - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test wallet balance - def test_get_balances(self) -> None: - wallet = self._get_wallet() - - try: - wallet.get_balance() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_balance(0) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_balance(0, 0) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_unlocked_balance() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_unlocked_balance(0) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_unlocked_balance(0, 0) - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test get wallet accounts - def test_get_accounts(self) -> None: - wallet = self._get_wallet() - - try: - wallet.get_accounts() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_accounts("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_accounts(True) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_accounts(True, "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_account(0) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_account(0, True) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.create_account() - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test get wallet subaddresses - def test_get_subaddresses(self) -> None: - wallet = self._get_wallet() - - try: - wallet.get_subaddress(0, 0) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_subaddresses(0) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.create_subaddress(0, "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.set_subaddress_label(1, 1, "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test txs wallet data - def test_txs_data(self) -> None: - wallet = self._get_wallet() - - try: - wallet.get_txs() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_transfers(MoneroTransferQuery()) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_outputs(MoneroOutputQuery()) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.export_outputs(True) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.import_outputs("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.export_key_images(True) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.import_key_images([]) - except Exception as e: - AssertUtils.assert_not_supported(e) - - # TODO segmentation fault -# try: -# wallet.get_new_key_images_from_last_import() -# except Exception as e: -# AssertUtils.assert_not_supported(e) - - try: - wallet.freeze_output("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.thaw_output("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.is_output_frozen("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_default_fee_priority() - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test wallet tx creation - def test_txs_creation(self) -> None: - wallet = self._get_wallet() - - try: - wallet.create_tx(MoneroTxConfig()) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.create_txs(MoneroTxConfig()) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.sweep_unlocked(MoneroTxConfig()) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.sweep_output(MoneroTxConfig()) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.sweep_dust(True) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.relay_tx("") - except Exception as e: - AssertUtils.assert_not_supported(e) - -# TODO aborted -# try: -# wallet.relay_tx(MoneroTxWallet()) -# except Exception as e: -# AssertUtils.assert_not_supported(e) - - try: - wallet.relay_txs([]) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.describe_tx_set(MoneroTxSet()) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.sign_txs("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.submit_txs("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_tx_key("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.check_tx_key("", "", "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test wallet proofs - def test_proofs(self) -> None: - wallet = self._get_wallet() - - try: - wallet.get_tx_proof("", "", "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.check_tx_proof("", "", "", "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_spend_proof("", "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.check_spend_proof("", "", "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_reserve_proof_wallet("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_reserve_proof_account(0, 0, "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.check_reserve_proof("", "", "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test wallet notes - def test_notes(self) -> None: - wallet = self._get_wallet() - - try: - wallet.get_tx_note("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.set_tx_note("", "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.set_tx_notes([], []) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_address_book_entries([]) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.add_address_book_entry("", "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.edit_address_book_entry(1, False, "", True, "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.delete_address_book_entry(0) - except Exception as e: - AssertUtils.assert_not_supported(e) - -# TODO segmentation fault -# try: -# wallet.tag_accounts("", []) -# except Exception as e: -# AssertUtils.assert_not_supported(e) - -# try: -# wallet.untag_accounts([]) -# except Exception as e: -# AssertUtils.assert_not_supported(e) - -# try: -# wallet.get_account_tags() -# except Exception as e: -# AssertUtils.assert_not_supported(e) - - try: - wallet.set_account_tag_label("", "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.set_account_label(0, "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_attribute("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.set_attribute("", "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test wallet multisig info - def test_multisig(self) -> None: - wallet = self._get_wallet() - - try: - wallet.is_multisig_import_needed() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.is_multisig() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_multisig_info() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.prepare_multisig() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.make_multisig([], 0, "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.exchange_multisig_keys([], "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.export_multisig_hex() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.import_multisig_hex([]) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.sign_multisig_tx_hex("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.submit_multisig_tx_hex("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - # Test wallet utils - def test_utils(self) -> None: - wallet = self._get_wallet() - - try: - wallet.sign_message("", MoneroMessageSignatureType.SIGN_WITH_VIEW_KEY) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.verify_message("", "", "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.get_payment_uri(MoneroTxConfig()) - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.parse_payment_uri("") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.start_mining() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.stop_mining() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.wait_for_next_block() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.change_password("", "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.move_to("", "") - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.save() - except Exception as e: - AssertUtils.assert_not_supported(e) - - try: - wallet.close() - except Exception as e: - AssertUtils.assert_not_supported(e) + @pytest.mark.not_supported + def test_is_view_only(self, wallet: MoneroWallet) -> None: + wallet.is_view_only() + + @pytest.mark.not_supported + def test_get_version(self, wallet: MoneroWallet) -> None: + wallet.get_version() + + @pytest.mark.not_supported + def test_get_path(self, wallet: MoneroWallet) -> None: + wallet.get_path() + + @pytest.mark.not_supported + def test_get_network_type(self, wallet: MoneroWallet) -> None: + wallet.get_network_type() + + # TODO move definitions to monero-cpp + #@pytest.mark.not_supported + def test_set_connection_manager(self, wallet: MoneroWallet) -> None: + wallet.set_connection_manager(MoneroConnectionManager()) + + @pytest.mark.skip(reason="TODO segmentation fault") + def test_get_connection_manager(self, wallet: MoneroWallet) -> None: + wallet.get_connection_manager() + + @pytest.mark.not_supported + def test_set_daemon_connection_1(self, wallet: MoneroWallet) -> None: + wallet.set_daemon_connection('') + + @pytest.mark.not_supported + def test_set_daemon_connection_2(self, wallet: MoneroWallet) -> None: + wallet.set_daemon_connection(MoneroRpcConnection()) + + @pytest.mark.not_supported + def test_get_daemon_connection(self, wallet: MoneroWallet) -> None: + wallet.get_daemon_connection() + + @pytest.mark.not_supported + def test_is_connected_to_daemon(self, wallet: MoneroWallet) -> None: + wallet.is_connected_to_daemon() + + @pytest.mark.not_supported + def test_is_daemon_trusted(self, wallet: MoneroWallet) -> None: + wallet.is_daemon_trusted() + + @pytest.mark.not_supported + def test_get_seed(self, wallet: MoneroWallet) -> None: + wallet.get_seed() + + @pytest.mark.not_supported + def test_get_seed_language(self, wallet: MoneroWallet) -> None: + wallet.get_seed_language() + + @pytest.mark.not_supported + def test_get_public_view_key(self, wallet: MoneroWallet) -> None: + wallet.get_public_view_key() + + @pytest.mark.not_supported + def test_get_private_view_key(self, wallet: MoneroWallet) -> None: + wallet.get_private_view_key() + + @pytest.mark.not_supported + def test_get_public_spend_key(self, wallet: MoneroWallet) -> None: + wallet.get_public_spend_key() + + @pytest.mark.not_supported + def test_get_private_spend_key(self, wallet: MoneroWallet) -> None: + wallet.get_private_spend_key() + + @pytest.mark.not_supported + def test_primary_address(self, wallet: MoneroWallet) -> None: + wallet.get_primary_address() + + @pytest.mark.not_supported + def test_get_address(self, wallet: MoneroWallet) -> None: + wallet.get_address(0, 0) + + @pytest.mark.not_supported + def test_get_address_index(self, wallet: MoneroWallet) -> None: + wallet.get_address_index("") + + @pytest.mark.not_supported + def test_get_integrated_address(self, wallet: MoneroWallet) -> None: + wallet.get_integrated_address() + + @pytest.mark.not_supported + def test_decode_integrated_address(self, wallet: MoneroWallet) -> None: + wallet.decode_integrated_address("") + + @pytest.mark.not_supported + def test_is_synced(self, wallet: MoneroWallet) -> None: + wallet.is_synced() + + @pytest.mark.not_supported + def test_get_height(self, wallet: MoneroWallet) -> None: + wallet.get_height() + + @pytest.mark.not_supported + def test_get_restore_height(self, wallet: MoneroWallet) -> None: + wallet.get_restore_height() + + @pytest.mark.not_supported + def test_set_restore_height(self, wallet: MoneroWallet) -> None: + wallet.set_restore_height(0) + + @pytest.mark.not_supported + def test_get_daemon_height(self, wallet: MoneroWallet) -> None: + wallet.get_daemon_height() + + @pytest.mark.not_supported + def test_get_daemon_max_peer_height(self, wallet: MoneroWallet) -> None: + wallet.get_daemon_max_peer_height() + + @pytest.mark.not_supported + def test_get_height_by_date(self, wallet: MoneroWallet) -> None: + wallet.get_height_by_date(0, 0, 0) + + @pytest.mark.not_supported + def test_add_listener(self, wallet: MoneroWallet) -> None: + wallet.add_listener(MoneroWalletListener()) + + @pytest.mark.not_supported + def test_remove_listener(self, wallet: MoneroWallet) -> None: + wallet.remove_listener(MoneroWalletListener()) + + @pytest.mark.not_supported + def test_get_listeners(self, wallet: MoneroWallet) -> None: + wallet.get_listeners() + + @pytest.mark.not_supported + def test_sync(self, wallet: MoneroWallet) -> None: + wallet.sync() + + @pytest.mark.not_supported + def test_sync_with_listener(self, wallet: MoneroWallet) -> None: + wallet.sync(MoneroWalletListener()) + + @pytest.mark.not_supported + def test_sync_with_start_height(self, wallet: MoneroWallet) -> None: + wallet.sync(0) + + @pytest.mark.not_supported + def test_sync_with_listener_and_start_height(self, wallet: MoneroWallet) -> None: + wallet.sync(0, MoneroWalletListener()) + + @pytest.mark.not_supported + def test_start_syncing(self, wallet: MoneroWallet) -> None: + wallet.start_syncing() + + @pytest.mark.not_supported + def test_stop_syncing(self, wallet: MoneroWallet) -> None: + wallet.stop_syncing() + + @pytest.mark.not_supported + def test_scan_txs(self, wallet: MoneroWallet) -> None: + wallet.scan_txs(["", "", ""]) + + @pytest.mark.not_supported + def test_rescan_spent(self, wallet: MoneroWallet) -> None: + wallet.rescan_spent() + + @pytest.mark.not_supported + def test_rescan_blockchain(self, wallet: MoneroWallet) -> None: + wallet.rescan_blockchain() + + @pytest.mark.not_supported + def test_get_balance(self, wallet: MoneroWallet) -> None: + wallet.get_balance() + + @pytest.mark.not_supported + def test_get_account_balance(self, wallet: MoneroWallet) -> None: + wallet.get_balance(0) + + @pytest.mark.not_supported + def test_get_subaddress_balance(self, wallet: MoneroWallet) -> None: + wallet.get_balance(0, 0) + + @pytest.mark.not_supported + def test_get_unlocked_balance(self, wallet: MoneroWallet) -> None: + wallet.get_unlocked_balance() + + @pytest.mark.not_supported + def test_get_account_unlocked_balance(self, wallet: MoneroWallet) -> None: + wallet.get_unlocked_balance(0) + + @pytest.mark.not_supported + def test_get_subaddress_unlocked_balance(self, wallet: MoneroWallet) -> None: + wallet.get_unlocked_balance(0, 0) + + @pytest.mark.not_supported + def test_get_accounts(self, wallet: MoneroWallet) -> None: + wallet.get_accounts() + + @pytest.mark.not_supported + def test_get_accounts_with_tag(self, wallet: MoneroWallet) -> None: + wallet.get_accounts("") + + @pytest.mark.not_supported + def test_get_accounts_with_subaddresses(self, wallet: MoneroWallet) -> None: + wallet.get_accounts(True) + + @pytest.mark.not_supported + def test_get_accounts_with_subaddresses_and_tag(self, wallet: MoneroWallet) -> None: + wallet.get_accounts(True, "") + + @pytest.mark.not_supported + def test_get_account(self, wallet: MoneroWallet) -> None: + wallet.get_account(0) + + @pytest.mark.not_supported + def test_get_account_with_subaddresses(self, wallet: MoneroWallet) -> None: + wallet.get_account(0, True) + + @pytest.mark.not_supported + def test_create_account(self, wallet: MoneroWallet) -> None: + wallet.create_account() + + @pytest.mark.not_supported + def test_get_subaddress(self, wallet: MoneroWallet) -> None: + wallet.get_subaddress(0, 0) + + @pytest.mark.not_supported + def test_get_account_subaddresses(self, wallet: MoneroWallet) -> None: + wallet.get_subaddresses(0) + + @pytest.mark.not_supported + def test_create_account_subaddress(self, wallet: MoneroWallet) -> None: + wallet.create_subaddress(0, "") + + @pytest.mark.not_supported + def test_set_subaddress_label(self, wallet: MoneroWallet) -> None: + wallet.set_subaddress_label(1, 1, "") + + @pytest.mark.not_supported + def test_get_txs(self, wallet: MoneroWallet) -> None: + wallet.get_txs() + + @pytest.mark.not_supported + def test_get_transfers(self, wallet: MoneroWallet) -> None: + wallet.get_transfers(MoneroTransferQuery()) + + @pytest.mark.not_supported + def test_get_outputs(self, wallet: MoneroWallet) -> None: + wallet.get_outputs(MoneroOutputQuery()) + + @pytest.mark.not_supported + def test_export_outputs(self, wallet: MoneroWallet) -> None: + wallet.export_outputs(True) + + @pytest.mark.not_supported + def test_import_outputs(self, wallet: MoneroWallet) -> None: + wallet.import_outputs("") + + @pytest.mark.not_supported + def test_export_key_images(self, wallet: MoneroWallet) -> None: + wallet.export_key_images(True) + + @pytest.mark.not_supported + def test_import_key_images(self, wallet: MoneroWallet) -> None: + wallet.import_key_images([]) + + #@pytest.mark.not_supported + @pytest.mark.skip(reason="TODO segmentation fault") + def test_get_new_key_images_from_last_import(self, wallet: MoneroWallet) -> None: + wallet.get_new_key_images_from_last_import() + + @pytest.mark.not_supported + def test_freeze_output(self, wallet: MoneroWallet) -> None: + wallet.freeze_output("") + + @pytest.mark.not_supported + def test_thaw_output(self, wallet: MoneroWallet) -> None: + wallet.thaw_output("") + + @pytest.mark.not_supported + def test_is_output_frozen(self, wallet: MoneroWallet) -> None: + wallet.is_output_frozen("") + + @pytest.mark.not_supported + def test_get_default_fee_priority(self, wallet: MoneroWallet) -> None: + wallet.get_default_fee_priority() + + @pytest.mark.not_supported + def test_create_tx(self, wallet: MoneroWallet) -> None: + wallet.create_tx(MoneroTxConfig()) + + @pytest.mark.not_supported + def test_create_txs(self, wallet: MoneroWallet) -> None: + wallet.create_txs(MoneroTxConfig()) + + @pytest.mark.not_supported + def test_sweep_unlocked(self, wallet: MoneroWallet) -> None: + wallet.sweep_unlocked(MoneroTxConfig()) + + @pytest.mark.not_supported + def test_sweep_output(self, wallet: MoneroWallet) -> None: + wallet.sweep_output(MoneroTxConfig()) + + @pytest.mark.not_supported + def test_sweep_dust(self, wallet: MoneroWallet) -> None: + wallet.sweep_dust(True) + + @pytest.mark.not_supported + def test_relay_tx_metadata(self, wallet: MoneroWallet) -> None: + wallet.relay_tx("") + + #@pytest.mark.not_supported + @pytest.mark.skip(reason="TODO aborted: insert check for tx.metadata != boost::none") + def test_relay_tx(self, wallet: MoneroWallet) -> None: + wallet.relay_tx(MoneroTxWallet()) + + @pytest.mark.not_supported + def test_relay_txs(self, wallet: MoneroWallet) -> None: + wallet.relay_txs([]) + + @pytest.mark.not_supported + def test_describe_tx_set(self, wallet: MoneroWallet) -> None: + wallet.describe_tx_set(MoneroTxSet()) + + @pytest.mark.not_supported + def test_sign_txs(self, wallet: MoneroWallet) -> None: + wallet.sign_txs("") + + @pytest.mark.not_supported + def test_submit_txs(self, wallet: MoneroWallet) -> None: + wallet.submit_txs("") + + @pytest.mark.not_supported + def test_get_tx_key(self, wallet: MoneroWallet) -> None: + wallet.get_tx_key("") + + @pytest.mark.not_supported + def test_check_tx_key(self, wallet: MoneroWallet) -> None: + wallet.check_tx_key("", "", "") + + @pytest.mark.not_supported + def test_get_tx_proof(self, wallet: MoneroWallet) -> None: + wallet.get_tx_proof("", "", "") + + @pytest.mark.not_supported + def test_check_tx_proof(self, wallet: MoneroWallet) -> None: + wallet.check_tx_proof("", "", "", "") + + @pytest.mark.not_supported + def test_get_spend_proof(self, wallet: MoneroWallet) -> None: + wallet.get_spend_proof("", "") + + @pytest.mark.not_supported + def test_check_spend_proof(self, wallet: MoneroWallet) -> None: + wallet.check_spend_proof("", "", "") + + @pytest.mark.not_supported + def test_get_reserve_proof_wallet(self, wallet: MoneroWallet) -> None: + wallet.get_reserve_proof_wallet("") + + @pytest.mark.not_supported + def test_get_reserve_proof_account(self, wallet: MoneroWallet) -> None: + wallet.get_reserve_proof_account(0, 0, "") + + @pytest.mark.not_supported + def test_check_reserve_proof(self, wallet: MoneroWallet) -> None: + wallet.check_reserve_proof("", "", "") + + @pytest.mark.not_supported + def test_get_tx_note(self, wallet: MoneroWallet) -> None: + wallet.get_tx_note("") + + @pytest.mark.not_supported + def test_set_tx_note(self, wallet: MoneroWallet) -> None: + wallet.set_tx_note("", "") + + @pytest.mark.not_supported + def test_set_tx_notes(self, wallet: MoneroWallet) -> None: + wallet.set_tx_notes([], []) + + @pytest.mark.not_supported + def test_get_address_book_entries(self, wallet: MoneroWallet) -> None: + wallet.get_address_book_entries([]) + + @pytest.mark.not_supported + def test_add_address_book_entry(self, wallet: MoneroWallet) -> None: + wallet.add_address_book_entry("", "") + + @pytest.mark.not_supported + def test_edit_address_book_entry(self, wallet: MoneroWallet) -> None: + wallet.edit_address_book_entry(1, False, "", True, "") + + @pytest.mark.not_supported + def test_delete_address_book_entry(self, wallet: MoneroWallet) -> None: + wallet.delete_address_book_entry(0) + + #@pytest.mark.not_supported + @pytest.mark.skip(reason="TODO segmentation fault") + def test_tag_accounts(self, wallet: MoneroWallet) -> None: + wallet.tag_accounts("", []) + + #@pytest.mark.not_supported + @pytest.mark.skip(reason="TODO segmentation fault") + def test_untag_accounts(self, wallet: MoneroWallet) -> None: + wallet.untag_accounts([]) + + #@pytest.mark.not_supported + @pytest.mark.skip(reason="TODO segmentation fault") + def test_get_account_tags(self, wallet: MoneroWallet) -> None: + wallet.get_account_tags() + + # TODO move definitions to monero-cpp + #@pytest.mark.not_supported + def test_set_account_tag_label(self, wallet: MoneroWallet) -> None: + wallet.set_account_tag_label("", "") + + # TODO move definitions to monero-cpp + #@pytest.mark.not_supported + def test_set_account_label(self, wallet: MoneroWallet) -> None: + wallet.set_account_label(0, "") + + @pytest.mark.not_supported + def test_get_attribute(self, wallet: MoneroWallet) -> None: + wallet.get_attribute("") + + @pytest.mark.not_supported + def test_set_attribute(self, wallet: MoneroWallet) -> None: + wallet.set_attribute("", "") + + @pytest.mark.not_supported + def test_is_multisig_import_needed(self, wallet: MoneroWallet) -> None: + wallet.is_multisig_import_needed() + + @pytest.mark.not_supported + def test_is_multisig(self, wallet: MoneroWallet) -> None: + wallet.is_multisig() + + @pytest.mark.not_supported + def test_get_multisig_info(self, wallet: MoneroWallet) -> None: + wallet.get_multisig_info() + + @pytest.mark.not_supported + def test_prepare_multisig(self, wallet: MoneroWallet) -> None: + wallet.prepare_multisig() + + @pytest.mark.not_supported + def test_make_multisig(self, wallet: MoneroWallet) -> None: + wallet.make_multisig([], 0, "") + + @pytest.mark.not_supported + def test_exchange_multisig_keys(self, wallet: MoneroWallet) -> None: + wallet.exchange_multisig_keys([], "") + + @pytest.mark.not_supported + def test_export_multisig_hex(self, wallet: MoneroWallet) -> None: + wallet.export_multisig_hex() + + @pytest.mark.not_supported + def test_import_multisig_hex(self, wallet: MoneroWallet) -> None: + wallet.import_multisig_hex([]) + + @pytest.mark.not_supported + def test_sign_multisig_tx_hex(self, wallet: MoneroWallet) -> None: + wallet.sign_multisig_tx_hex("") + + @pytest.mark.not_supported + def test_submit_multisig_tx_hex(self, wallet: MoneroWallet) -> None: + wallet.submit_multisig_tx_hex("") + + @pytest.mark.not_supported + def test_sign_message(self, wallet: MoneroWallet) -> None: + wallet.sign_message("", MoneroMessageSignatureType.SIGN_WITH_VIEW_KEY) + + @pytest.mark.not_supported + def test_verify_message(self, wallet: MoneroWallet) -> None: + wallet.verify_message("", "", "") + + @pytest.mark.not_supported + def test_get_payment_uri(self, wallet: MoneroWallet) -> None: + wallet.get_payment_uri(MoneroTxConfig()) + + @pytest.mark.not_supported + def test_parse_payment_uri(self, wallet: MoneroWallet) -> None: + wallet.parse_payment_uri("") + + @pytest.mark.not_supported + def test_start_mining(self, wallet: MoneroWallet) -> None: + wallet.start_mining() + + @pytest.mark.not_supported + def test_stop_mining(self, wallet: MoneroWallet) -> None: + wallet.stop_mining() + + @pytest.mark.not_supported + def test_wait_for_next_block(self, wallet: MoneroWallet) -> None: + wallet.wait_for_next_block() + + @pytest.mark.not_supported + def test_change_password(self, wallet: MoneroWallet) -> None: + wallet.change_password("", "") + + @pytest.mark.not_supported + def test_move_to(self, wallet: MoneroWallet) -> None: + wallet.move_to("", "") + + @pytest.mark.not_supported + def test_save(self, wallet: MoneroWallet) -> None: + wallet.save() + + @pytest.mark.not_supported + def test_close(self, wallet: MoneroWallet) -> None: + wallet.close() #endregion diff --git a/tests/test_monero_wallet_keys.py b/tests/test_monero_wallet_keys.py index 0d75411..500d7a3 100644 --- a/tests/test_monero_wallet_keys.py +++ b/tests/test_monero_wallet_keys.py @@ -5,7 +5,8 @@ from typing_extensions import override from monero import ( MoneroWalletKeys, MoneroWalletConfig, MoneroWallet, - MoneroUtils, MoneroAccount, MoneroSubaddress + MoneroUtils, MoneroAccount, MoneroSubaddress, + MoneroError, MoneroDaemonRpc, MoneroDaemon ) from utils import TestUtils as Utils, AssertUtils, WalletUtils @@ -14,17 +15,29 @@ logger: logging.Logger = logging.getLogger("TestMoneroWalletKeys") +@pytest.mark.unit class TestMoneroWalletKeys(BaseTestMoneroWallet): + """Keys-only wallet unit tests""" _account_indices: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] _subaddress_indices: list[int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - _wallet: MoneroWalletKeys = Utils.get_wallet_keys() # type: ignore @pytest.fixture(scope="class", autouse=True) @override def before_all(self): pass + @pytest.fixture(scope="class") + @override + def wallet(self) -> MoneroWalletKeys: + """Test keys wallet instance""" + return Utils.get_wallet_keys() + + @pytest.fixture(scope="class") + @override + def daemon(self) -> MoneroDaemonRpc: + return MoneroDaemon() # type: ignore + #region Overrides @classmethod @@ -68,151 +81,152 @@ def _close_wallet(self, wallet: MoneroWallet, save: bool = False) -> None: @override def _get_seed_languages(self) -> list[str]: - return self._wallet.get_seed_languages() + return self.get_test_wallet().get_seed_languages() @override - def get_test_wallet(self) -> MoneroWallet: + def get_test_wallet(self) -> MoneroWalletKeys: return Utils.get_wallet_keys() #endregion - #region Disabled Tests + #region Not Supported Tests - @pytest.mark.skip(reason="Txs not supported") + @pytest.mark.not_supported + @override def test_get_txs_wallet(self) -> None: return super().test_get_txs_wallet() - @pytest.mark.skip(reason="Daemon not supported") + @pytest.mark.not_supported @override - def test_daemon(self) -> None: - return super().test_daemon() + def test_daemon(self, wallet: MoneroWallet) -> None: + return super().test_daemon(wallet) - @pytest.mark.skip(reason="Daemon not supported") + @pytest.mark.not_supported @override - def test_get_daemon_max_peer_height(self) -> None: - return super().test_get_daemon_max_peer_height() + def test_get_daemon_max_peer_height(self, wallet: MoneroWallet) -> None: + return super().test_get_daemon_max_peer_height(wallet) - @pytest.mark.skip(reason="Wallet path not supported") + @pytest.mark.not_supported @override def test_get_path(self) -> None: return super().test_get_path() - @pytest.mark.skip(reason="Connection not supported") + @pytest.mark.not_supported @override def test_set_daemon_connection(self): return super().test_set_daemon_connection() - @pytest.mark.skip(reason="Sync not supported") + @pytest.mark.not_supported @override - def test_sync_without_progress(self): - return super().test_sync_without_progress() + def test_sync_without_progress(self, daemon: MoneroDaemonRpc, wallet: MoneroWallet): + return super().test_sync_without_progress(daemon, wallet) - @pytest.mark.skip(reason="Subaddress lookahead not supported") + @pytest.mark.not_supported @override - def test_subaddress_lookahead(self) -> None: - return super().test_subaddress_lookahead() + def test_subaddress_lookahead(self, wallet: MoneroWallet) -> None: + return super().test_subaddress_lookahead(wallet) - @pytest.mark.skip(reason="Not implemented") + @pytest.mark.xfail(raises=MoneroError, reason="monero_wallet_keys::get_integrated_address() not implemented") @override - def test_decode_integrated_address(self): - return super().test_decode_integrated_address() + def test_decode_integrated_address(self, wallet: MoneroWallet): + return super().test_decode_integrated_address(wallet) - @pytest.mark.skip(reason="Get address index not supported") + @pytest.mark.not_supported @override - def test_get_address_indices(self): - return super().test_get_address_indices() + def test_get_address_indices(self, wallet: MoneroWallet): + return super().test_get_address_indices(wallet) - @pytest.mark.skip(reason="Not supported") + @pytest.mark.not_supported @override - def test_wallet_equality_ground_truth(self): - return super().test_wallet_equality_ground_truth() + def test_wallet_equality_ground_truth(self, daemon: MoneroDaemonRpc, wallet: MoneroWallet): + return super().test_wallet_equality_ground_truth(daemon, wallet) - @pytest.mark.skip(reason="Not supported") + @pytest.mark.not_supported @override - def test_get_height(self): - return super().test_get_height() + def test_get_height(self, wallet: MoneroWallet): + return super().test_get_height(wallet) - @pytest.mark.skip(reason="Not supported") + @pytest.mark.not_supported @override - def test_get_height_by_date(self): - return super().test_get_height_by_date() + def test_get_height_by_date(self, wallet: MoneroWallet): + return super().test_get_height_by_date(wallet) - @pytest.mark.skip(reason="Not supported") + @pytest.mark.not_supported @override - def test_get_all_balances(self): - return super().test_get_all_balances() + def test_get_all_balances(self, wallet: MoneroWallet): + return super().test_get_all_balances(wallet) - @pytest.mark.skip(reason="Account creation not supported") + @pytest.mark.not_supported @override - def test_create_account_without_label(self): - return super().test_create_account_without_label() + def test_create_account_without_label(self, wallet: MoneroWallet): + return super().test_create_account_without_label(wallet) - @pytest.mark.skip(reason="Account/label creation not supported") + @pytest.mark.not_supported @override - def test_create_account_with_label(self): - return super().test_create_account_with_label() + def test_create_account_with_label(self, wallet: MoneroWallet): + return super().test_create_account_with_label(wallet) - @pytest.mark.skip(reason="Label creation not supported") + @pytest.mark.not_supported @override - def test_set_account_label(self): - return super().test_set_account_label() + def test_set_account_label(self, wallet: MoneroWallet): + return super().test_set_account_label(wallet) - @pytest.mark.skip(reason="Not supported") + @pytest.mark.not_supported @override - def test_get_subaddresses_by_indices(self): - return super().test_get_subaddresses_by_indices() + def test_get_subaddresses_by_indices(self, wallet: MoneroWallet): + return super().test_get_subaddresses_by_indices(wallet) - @pytest.mark.skip(reason="Subaddress creation not supported") + @pytest.mark.not_supported @override - def test_create_subaddress(self): - return super().test_create_subaddress() + def test_create_subaddress(self, wallet: MoneroWallet): + return super().test_create_subaddress(wallet) - @pytest.mark.skip(reason="Labels not supported") + @pytest.mark.xfail(raises=MoneroError, reason="Keys-only wallet does not have enumerable set of subaddresses") @override - def test_set_subaddress_label(self): - return super().test_set_subaddress_label() + def test_set_subaddress_label(self, wallet: MoneroWallet): + return super().test_set_subaddress_label(wallet) - @pytest.mark.skip(reason="Tx note not supported") + @pytest.mark.not_supported @override - def test_set_tx_note(self) -> None: - return super().test_set_tx_note() + def test_set_tx_note(self, wallet: MoneroWallet) -> None: + return super().test_set_tx_note(wallet) - @pytest.mark.skip(reason="Tx note not supported") + @pytest.mark.not_supported @override - def test_set_tx_notes(self) -> None: - return super().test_set_tx_notes() + def test_set_tx_notes(self, wallet: MoneroWallet) -> None: + return super().test_set_tx_notes(wallet) - @pytest.mark.skip(reason="Export key images not supported") + @pytest.mark.not_supported @override - def test_export_key_images(self): - return super().test_export_key_images() + def test_export_key_images(self, wallet: MoneroWallet): + return super().test_export_key_images(wallet) - @pytest.mark.skip(reason="Import key images not supported") + @pytest.mark.not_supported @override - def test_get_new_key_images_from_last_import(self): - return super().test_get_new_key_images_from_last_import() + def test_get_new_key_images_from_last_import(self, wallet: MoneroWallet): + return super().test_get_new_key_images_from_last_import(wallet) - @pytest.mark.skip(reason="Import key images not supported") + @pytest.mark.not_supported @override - def test_import_key_images(self): - return super().test_import_key_images() + def test_import_key_images(self, wallet: MoneroWallet): + return super().test_import_key_images(wallet) - @pytest.mark.skip(reason="Payment uri not supported") + @pytest.mark.not_supported @override - def test_get_payment_uri(self): - return super().test_get_payment_uri() + def test_get_payment_uri(self, wallet: MoneroWallet): + return super().test_get_payment_uri(wallet) - @pytest.mark.skip(reason="Mining not supported") + @pytest.mark.not_supported @override - def test_mining(self): - return super().test_mining() + def test_mining(self, daemon: MoneroDaemonRpc, wallet: MoneroWallet): + return super().test_mining(daemon, wallet) - @pytest.mark.skip(reason="Password not supported") + @pytest.mark.not_supported @override def test_change_password(self) -> None: return super().test_change_password() - @pytest.mark.skip(reason="Close and save not supported") + @pytest.mark.not_supported @override def test_save_and_close(self) -> None: return super().test_save_and_close() @@ -227,176 +241,115 @@ def test_create_wallet_random(self) -> None: """ Can create a random wallet. """ - e1: Exception | None = None - try: - config = MoneroWalletConfig() - wallet = self._create_wallet(config) - e2: Exception | None = None - - try: - MoneroUtils.validate_address(wallet.get_primary_address(), Utils.NETWORK_TYPE) - MoneroUtils.validate_private_view_key(wallet.get_private_view_key()) - MoneroUtils.validate_private_spend_key(wallet.get_private_spend_key()) - MoneroUtils.validate_mnemonic(wallet.get_seed()) - # TODO monero-wallet-rpc: get seed language - AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) - except Exception as e: - e2 = e - - if e2 is not None: - raise e2 + config = MoneroWalletConfig() + wallet = self._create_wallet(config) - # attempt to create wallet with unknown language - try: - config = MoneroWalletConfig() - config.language = "english" - self._create_wallet(config) - raise Exception("Should have thrown error") - except Exception as e: - AssertUtils.assert_equals("Unknown language: english", str(e)) + # validate wallet + MoneroUtils.validate_address(wallet.get_primary_address(), Utils.NETWORK_TYPE) + MoneroUtils.validate_private_view_key(wallet.get_private_view_key()) + MoneroUtils.validate_private_spend_key(wallet.get_private_spend_key()) + MoneroUtils.validate_mnemonic(wallet.get_seed()) + # TODO monero-wallet-rpc: get seed language + AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) + # attempt to create wallet with unknown language + try: + config = MoneroWalletConfig() + config.language = "english" + self._create_wallet(config) + raise Exception("Should have thrown error") except Exception as e: - e1 = e - - if e1 is not None: - raise e1 + AssertUtils.assert_equals("Unknown language: english", str(e)) @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @override - def test_create_wallet_from_seed(self, test_config: BaseTestMoneroWallet.Config) -> None: - e1: Exception | None = None - try: + def test_create_wallet_from_seed(self, wallet: MoneroWallet, test_config: BaseTestMoneroWallet.Config) -> None: + # save for comparison + primary_address = wallet.get_primary_address() + private_view_key = wallet.get_private_view_key() + private_spend_key = wallet.get_private_spend_key() - # save for comparison - primary_address = self._wallet.get_primary_address() - private_view_key = self._wallet.get_private_view_key() - private_spend_key = self._wallet.get_private_spend_key() + # recreate test wallet from seed + config = MoneroWalletConfig() + config.seed = Utils.SEED + w: MoneroWallet = self._create_wallet(config) - # recreate test wallet from seed - config = MoneroWalletConfig() - config.seed = Utils.SEED - - wallet: MoneroWallet = self._create_wallet(config) - e2: Exception | None = None - try: - AssertUtils.assert_equals(primary_address, wallet.get_primary_address()) - AssertUtils.assert_equals(private_view_key, wallet.get_private_view_key()) - AssertUtils.assert_equals(private_spend_key, wallet.get_private_spend_key()) - AssertUtils.assert_equals(Utils.SEED, wallet.get_seed()) - AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) - except Exception as e: - e2 = e - - if e2 is not None: - raise e2 - - # attempt to create wallet with two missing words - try: - config = MoneroWalletConfig() - config.seed = test_config.seed - self._create_wallet(config) - except Exception as e: - AssertUtils.assert_equals("Invalid mnemonic", str(e)) + AssertUtils.assert_equals(primary_address, w.get_primary_address()) + AssertUtils.assert_equals(private_view_key, w.get_private_view_key()) + AssertUtils.assert_equals(private_spend_key, w.get_private_spend_key()) + AssertUtils.assert_equals(Utils.SEED, w.get_seed()) + AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, w.get_seed_language()) + # attempt to create wallet with two missing words + try: + config = MoneroWalletConfig() + config.seed = test_config.seed + self._create_wallet(config) except Exception as e: - e1 = e - - if e1 is not None: - raise e1 + AssertUtils.assert_equals("Invalid mnemonic", str(e)) @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @override def test_create_wallet_from_seed_with_offset(self) -> None: - e1: Exception | None = None - try: - # create test wallet with offset - config = MoneroWalletConfig() - config.seed = Utils.SEED - config.seed_offset = "my secret offset!" - wallet: MoneroWallet = self._create_wallet(config) - e2: Exception | None = None - try: - MoneroUtils.validate_mnemonic(wallet.get_seed()) - AssertUtils.assert_not_equals(Utils.SEED, wallet.get_seed()) - MoneroUtils.validate_address(wallet.get_primary_address(), Utils.NETWORK_TYPE) - AssertUtils.assert_not_equals(Utils.ADDRESS, wallet.get_primary_address()) - # TODO monero-wallet-rpc: support - AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) - except Exception as e: - e2 = e - - if e2 is not None: - raise e2 - except Exception as e: - e1 = e - - if e1 is not None: - raise e1 + # create test wallet with offset + config = MoneroWalletConfig() + config.seed = Utils.SEED + config.seed_offset = "my secret offset!" + wallet: MoneroWallet = self._create_wallet(config) + + MoneroUtils.validate_mnemonic(wallet.get_seed()) + AssertUtils.assert_not_equals(Utils.SEED, wallet.get_seed()) + MoneroUtils.validate_address(wallet.get_primary_address(), Utils.NETWORK_TYPE) + AssertUtils.assert_not_equals(Utils.ADDRESS, wallet.get_primary_address()) + # TODO monero-wallet-rpc: support + AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @override - def test_create_wallet_from_keys(self) -> None: - e1: Exception | None = None - try: - # save for comparison - primary_address = self._wallet.get_primary_address() - private_view_key = self._wallet.get_private_view_key() - private_spend_key = self._wallet.get_private_spend_key() + def test_create_wallet_from_keys(self, daemon: MoneroDaemonRpc, wallet: MoneroWallet) -> None: + # save for comparison + primary_address = wallet.get_primary_address() + private_view_key = wallet.get_private_view_key() + private_spend_key = wallet.get_private_spend_key() - # recreate test wallet from keys - config = MoneroWalletConfig() - config.primary_address = primary_address - config.private_view_key = private_view_key - config.private_spend_key = private_spend_key - wallet: MoneroWallet = self._create_wallet(config) - e2: Exception | None = None - try: - AssertUtils.assert_equals(primary_address, wallet.get_primary_address()) - AssertUtils.assert_equals(private_view_key, wallet.get_private_view_key()) - AssertUtils.assert_equals(private_spend_key, wallet.get_private_spend_key()) - # TODO monero-wallet-rpc: cannot get seed from wallet created from keys? - MoneroUtils.validate_mnemonic(wallet.get_seed()) - AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) - - except Exception as e: - e2 = e - - self._close_wallet(wallet) - if e2 is not None: - raise e2 - - # recreate test wallet from spend key - config = MoneroWalletConfig() - config.primary_address = primary_address - config.private_spend_key = private_spend_key - wallet = self._create_wallet(config) - e2 = None - try: - AssertUtils.assert_equals(primary_address, wallet.get_primary_address()) - AssertUtils.assert_equals(private_view_key, wallet.get_private_view_key()) - AssertUtils.assert_equals(private_spend_key, wallet.get_private_spend_key()) - # TODO monero-wallet-rpc: cannot get seed from wallet created from keys? - MoneroUtils.validate_mnemonic(wallet.get_seed()) - AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, wallet.get_seed_language()) - - except Exception as e: - e2 = e - - self._close_wallet(wallet) - if e2 is not None: - raise e2 + # recreate test wallet from keys + config = MoneroWalletConfig() + config.primary_address = primary_address + config.private_view_key = private_view_key + config.private_spend_key = private_spend_key + w: MoneroWallet = self._create_wallet(config) - except Exception as e: - e1 = e + try: + AssertUtils.assert_equals(primary_address, w.get_primary_address()) + AssertUtils.assert_equals(private_view_key, w.get_private_view_key()) + AssertUtils.assert_equals(private_spend_key, w.get_private_spend_key()) + # TODO monero-wallet-rpc: cannot get seed from wallet created from keys? + MoneroUtils.validate_mnemonic(w.get_seed()) + AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, w.get_seed_language()) + finally: + self._close_wallet(w) + + # recreate test wallet from spend key + config = MoneroWalletConfig() + config.primary_address = primary_address + config.private_spend_key = private_spend_key + w = self._create_wallet(config) - if e1 is not None: - raise e1 + try: + AssertUtils.assert_equals(primary_address, w.get_primary_address()) + AssertUtils.assert_equals(private_view_key, w.get_private_view_key()) + AssertUtils.assert_equals(private_spend_key, w.get_private_spend_key()) + # TODO monero-wallet-rpc: cannot get seed from wallet created from keys? + MoneroUtils.validate_mnemonic(w.get_seed()) + AssertUtils.assert_equals(MoneroWallet.DEFAULT_LANGUAGE, w.get_seed_language()) + finally: + self._close_wallet(w) @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @override - def test_get_subaddress_address(self): - AssertUtils.assert_equals(self._wallet.get_primary_address(), (self._wallet.get_address(0, 0))) - accounts = self._get_test_accounts(True) + def test_get_subaddress_address(self, wallet: MoneroWallet): + AssertUtils.assert_equals(wallet.get_primary_address(), (wallet.get_address(0, 0))) + accounts = self._get_test_accounts(wallet, True) for account in accounts: assert account is not None @@ -407,55 +360,55 @@ def test_get_subaddress_address(self): for subaddress in account.subaddresses: assert subaddress is not None assert subaddress.index is not None - AssertUtils.assert_equals(subaddress.address, self._wallet.get_address(account.index, subaddress.index)) + AssertUtils.assert_equals(subaddress.address, wallet.get_address(account.index, subaddress.index)) @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @override - def test_get_subaddress_address_out_of_range(self): - accounts = self._get_test_accounts(True) + def test_get_subaddress_address_out_of_range(self, wallet: MoneroWallet): + accounts = self._get_test_accounts(wallet, True) account_idx = len(accounts) - 1 subaddress_idx = len(accounts[account_idx].subaddresses) - address = self._wallet.get_address(account_idx, subaddress_idx) + address = wallet.get_address(account_idx, subaddress_idx) AssertUtils.assert_not_none(address) AssertUtils.assert_true(len(address) > 0) @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @override - def test_get_account(self): - accounts = self._get_test_accounts() + def test_get_account(self, wallet: MoneroWallet): + accounts = self._get_test_accounts(wallet) assert len(accounts) > 0 for account in accounts: WalletUtils.test_account(account, Utils.NETWORK_TYPE, False) # test without subaddresses assert account.index is not None - retrieved = self._wallet.get_account(account.index) + retrieved = wallet.get_account(account.index) assert len(retrieved.subaddresses) == 0 # test with subaddresses - retrieved = self._wallet.get_account(account.index) - retrieved.subaddresses = self._wallet.get_subaddresses(account.index, self._subaddress_indices) + retrieved = wallet.get_account(account.index) + retrieved.subaddresses = wallet.get_subaddresses(account.index, self._subaddress_indices) @override - def test_get_accounts_without_subaddresses(self): - accounts = self._get_test_accounts() + def test_get_accounts_without_subaddresses(self, wallet: MoneroWallet): + accounts = self._get_test_accounts(wallet) assert len(accounts) > 0 for account in accounts: WalletUtils.test_account(account, Utils.NETWORK_TYPE, False) assert len(account.subaddresses) == 0 @override - def test_get_accounts_with_subaddresses(self): - accounts = self._get_test_accounts(True) + def test_get_accounts_with_subaddresses(self, wallet: MoneroWallet): + accounts = self._get_test_accounts(wallet, True) assert len(accounts) > 0 for account in accounts: WalletUtils.test_account(account, Utils.NETWORK_TYPE, False) assert len(account.subaddresses) > 0 @override - def test_get_subaddresses(self): - wallet = self._wallet - accounts = self._get_test_accounts() + def test_get_subaddresses(self, wallet: MoneroWallet): + wallet = wallet + accounts = self._get_test_accounts(wallet) assert len(accounts) > 0 for account in accounts: assert account.index is not None @@ -466,45 +419,45 @@ def test_get_subaddresses(self): assert account.index == subaddress.account_index @override - def test_get_subaddress_by_index(self): - accounts = self._get_test_accounts() + def test_get_subaddress_by_index(self, wallet: MoneroWallet): + accounts = self._get_test_accounts(wallet) assert len(accounts) > 0 for account in accounts: assert account.index is not None - subaddresses = self._wallet.get_subaddresses(account.index, self._subaddress_indices) + subaddresses = wallet.get_subaddresses(account.index, self._subaddress_indices) assert len(subaddresses) > 0 for subaddress in subaddresses: assert subaddress.index is not None WalletUtils.test_subaddress(subaddress, False) - AssertUtils.assert_subaddress_equal(subaddress, self._get_subaddress(account.index, subaddress.index)) + AssertUtils.assert_subaddress_equal(subaddress, self._get_subaddress(wallet, account.index, subaddress.index)) # test plural call with single subaddr number AssertUtils.assert_subaddress_equal( - subaddress, self._wallet.get_subaddresses(account.index, [subaddress.index])[0] + subaddress, wallet.get_subaddresses(account.index, [subaddress.index])[0] ) #endregion #region Utils - def _get_subaddress(self, account_idx: int, subaddress_idx: int) -> Optional[MoneroSubaddress]: + def _get_subaddress(self, wallet: MoneroWallet, account_idx: int, subaddress_idx: int) -> Optional[MoneroSubaddress]: subaddress_indices: list[int] = [subaddress_idx] - subaddresses = self._wallet.get_subaddresses(account_idx, subaddress_indices) + subaddresses = wallet.get_subaddresses(account_idx, subaddress_indices) if len(subaddresses) == 0: return None return subaddresses[0] - def _get_test_accounts(self, include_subaddresses: bool = False) -> list[MoneroAccount]: + def _get_test_accounts(self, wallet: MoneroWallet, include_subaddresses: bool = False) -> list[MoneroAccount]: account_indices = self._account_indices subaddress_indices = self._subaddress_indices accounts: list[MoneroAccount] = [] for account_idx in account_indices: - account = self._wallet.get_account(account_idx) + account = wallet.get_account(account_idx) if include_subaddresses: - account.subaddresses = self._wallet.get_subaddresses(account_idx, subaddress_indices) + account.subaddresses = wallet.get_subaddresses(account_idx, subaddress_indices) accounts.append(account) diff --git a/tests/test_monero_wallet_rpc.py b/tests/test_monero_wallet_rpc.py index 930fb45..d9ab6df 100644 --- a/tests/test_monero_wallet_rpc.py +++ b/tests/test_monero_wallet_rpc.py @@ -4,39 +4,43 @@ from monero import MoneroWallet, MoneroWalletConfig, MoneroDaemonRpc, MoneroWalletRpc from typing_extensions import override -from utils import TestUtils as Utils, OsUtils +from utils import TestUtils as Utils from test_monero_wallet_common import BaseTestMoneroWallet logger: logging.Logger = logging.getLogger("TestMoneroWalletRpc") -@pytest.mark.skipif(OsUtils.is_windows(), reason="TODO setup test environment for windows") +@pytest.mark.integration class TestMoneroWalletRpc(BaseTestMoneroWallet): - - _daemon: MoneroDaemonRpc = Utils.get_daemon_rpc() - _wallet: MoneroWalletRpc = Utils.get_wallet_rpc() # type: ignore + """Rpc wallet integration tests""" #region Overrides + @pytest.fixture(scope="class") @override - def get_test_wallet(self) -> MoneroWallet: + def wallet(self) -> MoneroWalletRpc: + """Test rpc wallet instance""" + return Utils.get_wallet_rpc() + + @override + def get_test_wallet(self) -> MoneroWalletRpc: return Utils.get_wallet_rpc() @override def _open_wallet(self, config: MoneroWalletConfig | None) -> MoneroWallet: try: return Utils.open_wallet_rpc(config) - except Exception as e: + except Exception: Utils.free_wallet_rpc_resources() - raise e + raise @override def _create_wallet(self, config: MoneroWalletConfig) -> MoneroWallet: try: return Utils.create_wallet_rpc(config) - except Exception as e: + except Exception: Utils.free_wallet_rpc_resources() - raise e + raise @override def _close_wallet(self, wallet: MoneroWallet, save: bool = False) -> None: @@ -45,7 +49,7 @@ def _close_wallet(self, wallet: MoneroWallet, save: bool = False) -> None: @override def _get_seed_languages(self) -> list[str]: - return self._wallet.get_seed_languages() # type: ignore + return self.get_test_wallet().get_seed_languages() @override def after_each(self, request: pytest.FixtureRequest): @@ -63,94 +67,101 @@ def get_daemon_rpc_uri(self) -> str: # Can indicate if multisig import is needed for correct balance information #@pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") @pytest.mark.skip("TODO") - def test_is_multisig_needed(self): + def test_is_multisig_needed(self, wallet: MoneroWallet): # TODO test with multisig wallet - assert self._wallet.is_multisig_import_needed() is False, "Expected non-multisig wallet" + assert wallet.is_multisig_import_needed() is False, "Expected non-multisig wallet" # Can save the wallet @pytest.mark.skipif(Utils.TEST_NON_RELAYS is False, reason="TEST_NON_RELAYS disabled") - def test_save(self): - self._wallet.save() + def test_save(self, wallet: MoneroWallet): + wallet.save() #endregion - #region Disabled Tests + #region Not Supported Tests - @pytest.mark.skip(reason="TODO implement get_txs") - def test_get_txs_wallet(self) -> None: - return super().test_get_txs_wallet() + @pytest.mark.not_supported + @override + def test_get_daemon_max_peer_height(self, wallet: MoneroWallet) -> None: + return super().test_get_daemon_max_peer_height(wallet) - @pytest.mark.skip(reason="Not implemented for wallet rpc") - def test_get_daemon_max_peer_height(self) -> None: - return super().test_get_daemon_max_peer_height() + @pytest.mark.not_supported + @override + def test_daemon(self, wallet: MoneroWallet) -> None: + return super().test_daemon(wallet) - @pytest.mark.skip(reason="Not supported") - def test_daemon(self) -> None: - return super().test_daemon() + @pytest.mark.not_supported + @override + def test_get_seed_language(self, wallet: MoneroWallet): + return super().test_get_seed_language(wallet) - @pytest.mark.skip(reason="Not supported") + @pytest.mark.not_supported @override - def test_get_seed_language(self): - return super().test_get_seed_language() + def test_get_height_by_date(self, wallet: MoneroWallet): + return super().test_get_height_by_date(wallet) - @pytest.mark.skip(reason="Not supported") + #endregion + + #region Disabled Tests + + @pytest.mark.skip(reason="TODO implement get_txs") @override - def test_get_height_by_date(self): - return super().test_get_height_by_date() + def test_get_txs_wallet(self) -> None: + return super().test_get_txs_wallet() @pytest.mark.skip(reason="TODO monero-project") @override - def test_get_public_view_key(self): - return super().test_get_public_view_key() + def test_get_public_view_key(self, wallet: MoneroWallet): + return super().test_get_public_view_key(wallet) @pytest.mark.skip(reason="TODO monero-project") @override - def test_get_public_spend_key(self): - return super().test_get_public_spend_key() + def test_get_public_spend_key(self, wallet: MoneroWallet): + return super().test_get_public_spend_key(wallet) @pytest.mark.skip(reason="TODO") @override - def test_wallet_equality_ground_truth(self): - return super().test_wallet_equality_ground_truth() + def test_wallet_equality_ground_truth(self, daemon: MoneroDaemonRpc, wallet: MoneroWallet): + return super().test_wallet_equality_ground_truth(daemon, wallet) @pytest.mark.skip(reason="TODO") @override - def test_get_payment_uri(self): - return super().test_get_payment_uri() + def test_get_payment_uri(self, wallet: MoneroWallet): + return super().test_get_payment_uri(wallet) @pytest.mark.skip(reason="TODO") @override - def test_set_tx_note(self) -> None: - return super().test_set_tx_note() + def test_set_tx_note(self, wallet: MoneroWallet) -> None: + return super().test_set_tx_note(wallet) @pytest.mark.skip(reason="TODO") @override - def test_set_tx_notes(self): - return super().test_set_tx_notes() + def test_set_tx_notes(self, wallet: MoneroWallet): + return super().test_set_tx_notes(wallet) @pytest.mark.skip(reason="TODO") @override - def test_export_key_images(self): - return super().test_export_key_images() + def test_export_key_images(self, wallet: MoneroWallet): + return super().test_export_key_images(wallet) @pytest.mark.skip(reason="TODO (monero-project): https://github.com/monero-project/monero/issues/5812") @override - def test_import_key_images(self): - return super().test_import_key_images() + def test_import_key_images(self, wallet: MoneroWallet): + return super().test_import_key_images(wallet) @pytest.mark.skip(reason="TODO") @override - def test_get_new_key_images_from_last_import(self): - return super().test_get_new_key_images_from_last_import() + def test_get_new_key_images_from_last_import(self, wallet: MoneroWallet): + return super().test_get_new_key_images_from_last_import(wallet) @pytest.mark.skip(reason="TODO") @override - def test_subaddress_lookahead(self) -> None: - return super().test_subaddress_lookahead() + def test_subaddress_lookahead(self, wallet: MoneroWallet) -> None: + return super().test_subaddress_lookahead(wallet) @pytest.mark.skip(reason="TODO") @override - def test_get_subaddress_address_out_of_range(self): - return super().test_get_subaddress_address_out_of_range() + def test_get_subaddress_address_out_of_range(self, wallet: MoneroWallet): + return super().test_get_subaddress_address_out_of_range(wallet) #endregion diff --git a/tests/utils/assert_utils.py b/tests/utils/assert_utils.py index b35795a..f44fe7c 100644 --- a/tests/utils/assert_utils.py +++ b/tests/utils/assert_utils.py @@ -55,14 +55,6 @@ def equals(cls, expr1: Any, expr2: Any) -> bool: def assert_not_equals(cls, expr1: Any, expr2: Any, message: str = "assertion failed"): assert expr1 != expr2, f"{message}: {expr1} != {expr2}" - @classmethod - def assert_is(cls, expr: Any, what: Any, message: str = "assertion failed"): - assert expr is what, f"{message}: {expr} is {what}" - - @classmethod - def assert_not_supported(cls, error: Any) -> None: - assert "not supported" in str(error), f"Expected not supported method: {error}" - @classmethod def assert_connection_equals(cls, c1: Optional[MoneroRpcConnection], c2: Optional[MoneroRpcConnection]) -> None: if c1 is None and c2 is None: diff --git a/tests/utils/test_utils.py b/tests/utils/test_utils.py index b93d3ff..c5cb0af 100644 --- a/tests/utils/test_utils.py +++ b/tests/utils/test_utils.py @@ -14,7 +14,6 @@ from .wallet_sync_printer import WalletSyncPrinter from .wallet_tx_tracker import WalletTxTracker from .gen_utils import GenUtils -from .os_utils import OsUtils from .string_utils import StringUtils from .assert_utils import AssertUtils from .daemon_utils import DaemonUtils @@ -248,8 +247,6 @@ def create_test_wallets_dir(cls) -> None: @classmethod def get_daemon_rpc(cls) -> MoneroDaemonRpc: """Get test daemon rpc""" - if OsUtils.is_windows(): - return None # type: ignore if cls._DAEMON_RPC is None: cls._DAEMON_RPC = MoneroDaemonRpc(cls.DAEMON_RPC_URI, cls.DAEMON_RPC_USERNAME, cls.DAEMON_RPC_PASSWORD) @@ -294,8 +291,6 @@ def get_wallet_full_config(cls, daemon_connection: MoneroRpcConnection) -> Moner @classmethod def get_wallet_full(cls) -> MoneroWalletFull: """Get test wallet full""" - if OsUtils.is_windows(): - return None # type: ignore if cls._WALLET_FULL is None: # create wallet from seed if it doesn't exist @@ -366,8 +361,6 @@ def get_mining_wallet(cls) -> MoneroWalletFull: @classmethod def get_wallet_rpc(cls) -> MoneroWalletRpc: """Get test wallet rpc""" - if OsUtils.is_windows(): - return None # type: ignore if cls._WALLET_RPC is None: