Skip to content

Commit 8e9e167

Browse files
Test fixes
1 parent c97381e commit 8e9e167

File tree

7 files changed

+185
-73
lines changed

7 files changed

+185
-73
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ For example: `export LD_PRELOAD=/path/to/libjemalloc.a` then run your app.
132132
1. Download and install [Monero CLI](https://web.getmonero.org/downloads/).
133133
2. Start monerod, e.g.: `./monerod --stagenet` (or use a remote daemon).
134134
3. Start monero-wallet-rpc, e.g.: `./monero-wallet-rpc --daemon-address http://localhost:38081 --stagenet --rpc-bind-port 38083 --rpc-login rpc_user:abc123 --wallet-dir ./`
135-
4. Configure the appropriate RPC endpoints, authentication, and other settings in [test_utils.py](tests/test_utils.py).
135+
4. Configure the appropriate RPC endpoints, authentication, and other settings in [monero_test_utils.py](tests/utils/monero_test_utils.py).
136136
5. Run all *.py files in tests folder with `pytest`.
137137
138138

src/monero_bindings.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,10 @@ class PyMoneroDaemonRpc : public PyMoneroDaemon {
699699
m_rpc->set_credentials(username, password);
700700
}
701701

702+
std::shared_ptr<PyMoneroRpcConnection> get_rpc_connection() const {
703+
return m_rpc;
704+
}
705+
702706
std::shared_ptr<monero::monero_block> get_block_by_hash(const std::string& hash) override {
703707
auto result = std::make_shared<monero::monero_block>();
704708

@@ -2661,7 +2665,10 @@ PYBIND11_MODULE(monero, m) {
26612665
py::class_<PyMoneroDaemonRpc, PyMoneroDaemon, std::shared_ptr<PyMoneroDaemonRpc>>(m, "MoneroDaemonRpc")
26622666
.def(py::init<>())
26632667
.def(py::init<std::shared_ptr<PyMoneroRpcConnection>>(), py::arg("rpc"))
2664-
.def(py::init<std::string&, std::string&, std::string&>(), py::arg("uri"), py::arg("username") = "", py::arg("password") = "");
2668+
.def(py::init<std::string&, std::string&, std::string&>(), py::arg("uri"), py::arg("username") = "", py::arg("password") = "")
2669+
.def("get_rpc_connection", [](const PyMoneroRpcConnection& self) {
2670+
MONERO_CATCH_AND_RETHROW(self.get_rpc_connection());
2671+
});
26652672

26662673
// monero_wallet
26672674
py::class_<monero::monero_wallet, PyMoneroWallet, std::shared_ptr<monero::monero_wallet>>(m, "MoneroWallet")

tests/utils/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from .monero_test_utils import MoneroTestUtils
2+
from .wallet_sync_printer import WalletSyncPrinter

tests/utils/monero_test_utils.py

Lines changed: 155 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,164 @@
11
from typing import Any, Optional
2-
from monero import MoneroNetworkType, MoneroUtils
2+
from monero import MoneroNetworkType, MoneroUtils, MoneroWalletFull, MoneroRpcConnection, MoneroWalletConfig, MoneroDaemonRpc
3+
4+
from wallet_sync_printer import WalletSyncPrinter
35

46
class MoneroTestUtils:
7+
_WALLET_FULL: MoneroWalletFull
8+
# monero daemon rpc endpoint configuration (change per your configuration)
9+
DAEMON_RPC_URI: str = "localhost:28081"
10+
DAEMON_RPC_USERNAME: str = ""
11+
DAEMON_RPC_PASSWORD: str = ""
512

6-
@classmethod
7-
def assert_false(cls, expr: Any):
8-
assert expr == False
9-
10-
@classmethod
11-
def assert_true(cls, expr: Any):
12-
assert expr == True
13-
14-
@classmethod
15-
def assert_not_none(cls, expr: Any):
16-
assert expr is not None
17-
18-
@classmethod
19-
def test_invalid_address(cls, address: Optional[str], networkType: MoneroNetworkType) -> None:
20-
if address is None:
21-
return
22-
23-
cls.assert_false(MoneroUtils.is_valid_address(address, networkType))
24-
try:
25-
MoneroUtils.validate_address(address, networkType)
26-
raise Exception("Should have thrown exception")
27-
except Exception as e:
28-
cls.assert_false(len(str(e)) == 0)
29-
30-
@classmethod
31-
def test_invalid_private_view_key(cls, privateViewKey: Optional[str]):
32-
if privateViewKey is None:
33-
return
34-
35-
cls.assert_false(MoneroUtils.is_valid_private_view_key(privateViewKey));
36-
try:
37-
MoneroUtils.validate_private_view_key(privateViewKey);
38-
raise Exception("Should have thrown exception");
39-
except Exception as e:
40-
cls.assert_false(len(str(e)) == 0)
13+
# monero wallet rpc configuration (change per your configuration)
14+
WALLET_RPC_PORT_START: int = 28084 # test wallet executables will bind to consecutive ports after these
15+
WALLET_RPC_ZMQ_ENABLED: bool = False
16+
WALLET_RPC_ZMQ_PORT_START: int = 58083
17+
WALLET_RPC_ZMQ_BIND_PORT_START: int = 48083 # TODO: zmq bind port necessary?
18+
WALLET_RPC_USERNAME: str = "rpc_user"
19+
WALLET_RPC_PASSWORD: str = "abc123"
20+
WALLET_RPC_ZMQ_DOMAIN: str = "127.0.0.1"
21+
WALLET_RPC_DOMAIN: str = "localhost"
22+
WALLET_RPC_URI = WALLET_RPC_DOMAIN + ":" + str(WALLET_RPC_PORT_START)
23+
WALLET_RPC_ZMQ_URI = "tcp:#" + WALLET_RPC_ZMQ_DOMAIN + ":" + str(WALLET_RPC_ZMQ_PORT_START)
24+
WALLET_RPC_ACCESS_CONTROL_ORIGINS = "http:#localhost:8080" # cors access from web browser
25+
26+
# test wallet config
27+
WALLET_NAME = "test_wallet_1"
28+
WALLET_PASSWORD = "supersecretpassword123"
29+
TEST_WALLETS_DIR = "./test_wallets"
30+
WALLET_FULL_PATH = TEST_WALLETS_DIR + "/" + WALLET_NAME
31+
32+
# test wallet constants
33+
MAX_FEE = 7500000*10000
34+
NETWORK_TYPE: MoneroNetworkType = MoneroNetworkType.TESTNET
35+
LANGUAGE: str = "English"
36+
SEED: str = "silk mocked cucumber lettuce hope adrenalin aching lush roles fuel revamp baptism wrist long tender teardrop midst pastry pigment equip frying inbound pinched ravine frying"
37+
ADDRESS: str = "A1y9sbVt8nqhZAVm3me1U18rUVXcjeNKuBd1oE2cTs8biA9cozPMeyYLhe77nPv12JA3ejJN3qprmREriit2fi6tJDi99RR"
38+
FIRST_RECEIVE_HEIGHT: int = 171 # NOTE: this value must be the height of the wallet's first tx for tests
39+
SYNC_PERIOD_IN_MS: int = 5000 # period between wallet syncs in milliseconds
40+
OFFLINE_SERVER_URI: str = "offline_server_uri" # dummy server uri to remain offline because wallet2 connects to default if not given
41+
AUTO_CONNECT_TIMEOUT_MS: int = 3000
42+
43+
@classmethod
44+
def assert_false(cls, expr: Any):
45+
assert expr == False
46+
47+
@classmethod
48+
def assert_true(cls, expr: Any):
49+
assert expr == True
50+
51+
@classmethod
52+
def assert_not_none(cls, expr: Any):
53+
assert expr is not None
54+
55+
@classmethod
56+
def get_daemon_rpc(cls) -> MoneroDaemonRpc:
57+
raise Exception("not implemented")
58+
59+
@classmethod
60+
def get_wallet_full_config(cls, daemon_connection: MoneroRpcConnection) -> MoneroWalletConfig:
61+
config = MoneroWalletConfig()
62+
config.path = cls.WALLET_FULL_PATH
63+
config.password = cls.WALLET_PASSWORD
64+
config.network_type = cls.NETWORK_TYPE
65+
config.seed = cls.SEED
66+
config.server = daemon_connection
67+
config.restore_height = cls.FIRST_RECEIVE_HEIGHT
68+
69+
return config
4170

71+
@classmethod
72+
def get_wallet_full(cls) -> MoneroWalletFull:
73+
if cls._WALLET_FULL is None:
74+
# create wallet from seed if it doesn't exist
75+
if not MoneroWalletFull.wallet_exists(cls.WALLET_FULL_PATH):
76+
77+
# create directory for test wallets if it doesn't exist
78+
#File testWalletsDir = new File(TestUtils.TEST_WALLETS_DIR);
79+
#if (!testWalletsDir.exists()) testWalletsDir.mkdirs();
80+
81+
# create wallet with connection
82+
daemon_connection = MoneroRpcConnection(cls.DAEMON_RPC_URI, cls.DAEMON_RPC_USERNAME, cls.DAEMON_RPC_PASSWORD)
83+
config = cls.get_wallet_full_config(daemon_connection)
84+
cls._WALLET_FULL = MoneroWalletFull.create_wallet(config)
85+
assert MoneroTestUtils.FIRST_RECEIVE_HEIGHT, cls._WALLET_FULL.get_restore_height()
86+
assert daemon_connection == cls._WALLET_FULL.get_daemon_connection()
87+
88+
# otherwise open existing wallet and update daemon connection
89+
else:
90+
cls._WALLET_FULL = MoneroWalletFull.open_wallet(cls.WALLET_FULL_PATH, cls.WALLET_PASSWORD, cls.NETWORK_TYPE)
91+
cls._WALLET_FULL.set_daemon_connection(cls.get_daemon_rpc().get_rpc_connection())
4292

43-
@classmethod
44-
def test_invalid_public_view_key(cls, public_view_key: Optional[str]) -> None:
45-
if public_view_key is None:
46-
return
47-
48-
cls.assert_false(MoneroUtils.is_valid_public_view_key(public_view_key))
49-
try:
50-
MoneroUtils.validate_public_view_key(public_view_key)
51-
raise Exception("Should have thrown exception")
52-
except Exception as e:
53-
cls.assert_false(len(str(e)) == 0)
54-
55-
@classmethod
56-
def test_invalid_private_spend_key(cls, privateSpendKey: Optional[str]):
57-
if privateSpendKey is None:
58-
return
59-
60-
try:
61-
cls.assert_false(MoneroUtils.is_valid_private_spend_key(privateSpendKey))
62-
MoneroUtils.validate_private_spend_key(privateSpendKey)
63-
raise Exception("Should have thrown exception")
64-
except Exception as e:
65-
cls.assert_false(len(str(e)) == 0)
66-
67-
68-
@classmethod
69-
def test_invalid_public_spend_key(cls, publicSpendKey: Optional[str]):
70-
if publicSpendKey is None:
71-
return
93+
# sync and save wallet
94+
listener = WalletSyncPrinter()
95+
cls._WALLET_FULL.sync(listener)
96+
cls._WALLET_FULL.save()
97+
cls._WALLET_FULL.start_syncing(cls.SYNC_PERIOD_IN_MS) # start background synchronizing with sync period
7298

73-
cls.assert_false(MoneroUtils.is_valid_public_spend_key(publicSpendKey))
74-
try:
75-
MoneroUtils.validate_public_spend_key(publicSpendKey)
76-
raise Exception("Should have thrown exception")
77-
except Exception as e:
78-
cls.assert_false(len(str(e)) == 0)
99+
# ensure we're testing the right wallet
100+
assert MoneroTestUtils.SEED == cls._WALLET_FULL.get_seed()
101+
assert MoneroTestUtils.ADDRESS == cls._WALLET_FULL.get_primary_address()
102+
return cls._WALLET_FULL
103+
104+
@classmethod
105+
def test_invalid_address(cls, address: Optional[str], networkType: MoneroNetworkType) -> None:
106+
if address is None:
107+
return
108+
109+
cls.assert_false(MoneroUtils.is_valid_address(address, networkType))
110+
try:
111+
MoneroUtils.validate_address(address, networkType)
112+
raise Exception("Should have thrown exception")
113+
except Exception as e:
114+
cls.assert_false(len(str(e)) == 0)
115+
116+
@classmethod
117+
def test_invalid_private_view_key(cls, privateViewKey: Optional[str]):
118+
if privateViewKey is None:
119+
return
120+
121+
cls.assert_false(MoneroUtils.is_valid_private_view_key(privateViewKey))
122+
try:
123+
MoneroUtils.validate_private_view_key(privateViewKey)
124+
raise Exception("Should have thrown exception")
125+
except Exception as e:
126+
cls.assert_false(len(str(e)) == 0)
127+
128+
@classmethod
129+
def test_invalid_public_view_key(cls, public_view_key: Optional[str]) -> None:
130+
if public_view_key is None:
131+
return
132+
133+
cls.assert_false(MoneroUtils.is_valid_public_view_key(public_view_key))
134+
try:
135+
MoneroUtils.validate_public_view_key(public_view_key)
136+
raise Exception("Should have thrown exception")
137+
except Exception as e:
138+
cls.assert_false(len(str(e)) == 0)
139+
140+
@classmethod
141+
def test_invalid_private_spend_key(cls, privateSpendKey: Optional[str]):
142+
if privateSpendKey is None:
143+
return
144+
145+
try:
146+
cls.assert_false(MoneroUtils.is_valid_private_spend_key(privateSpendKey))
147+
MoneroUtils.validate_private_spend_key(privateSpendKey)
148+
raise Exception("Should have thrown exception")
149+
except Exception as e:
150+
cls.assert_false(len(str(e)) == 0)
151+
152+
@classmethod
153+
def test_invalid_public_spend_key(cls, publicSpendKey: Optional[str]):
154+
if publicSpendKey is None:
155+
return
156+
157+
cls.assert_false(MoneroUtils.is_valid_public_spend_key(publicSpendKey))
158+
try:
159+
MoneroUtils.validate_public_spend_key(publicSpendKey)
160+
raise Exception("Should have thrown exception")
161+
except Exception as e:
162+
cls.assert_false(len(str(e)) == 0)
79163

80164

tests/utils/wallet_sync_printer.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing_extensions import override
2+
from monero import MoneroWalletListener
3+
4+
class WalletSyncPrinter(MoneroWalletListener):
5+
6+
next_increment: float
7+
sync_resolution: float
8+
9+
def __init__(self, sync_resolution: float = 0.05) -> None:
10+
super().__init__()
11+
self.next_increment = 0
12+
self.sync_resolution = sync_resolution
13+
14+
@override
15+
def on_sync_progress(self, height: int, start_height: int, end_height: int, percent_done: float, message: str):
16+
if (percent_done == 1.0 or percent_done >= self.next_increment):
17+
msg = f"on_sync_progess({height}, {start_height}, {end_height}, {percent_done}, {message})"
18+
print(msg)
19+
self.next_increment += self.sync_resolution
20+

0 commit comments

Comments
 (0)