iwa 0.0.0__py3-none-any.whl → 0.0.1a2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- conftest.py +22 -0
- iwa/__init__.py +1 -0
- iwa/__main__.py +6 -0
- iwa/core/__init__.py +1 -0
- iwa/core/chain/__init__.py +68 -0
- iwa/core/chain/errors.py +47 -0
- iwa/core/chain/interface.py +514 -0
- iwa/core/chain/manager.py +38 -0
- iwa/core/chain/models.py +128 -0
- iwa/core/chain/rate_limiter.py +193 -0
- iwa/core/cli.py +210 -0
- iwa/core/constants.py +28 -0
- iwa/core/contracts/__init__.py +1 -0
- iwa/core/contracts/contract.py +297 -0
- iwa/core/contracts/erc20.py +79 -0
- iwa/core/contracts/multisend.py +71 -0
- iwa/core/db.py +317 -0
- iwa/core/keys.py +361 -0
- iwa/core/mnemonic.py +385 -0
- iwa/core/models.py +344 -0
- iwa/core/monitor.py +209 -0
- iwa/core/plugins.py +45 -0
- iwa/core/pricing.py +91 -0
- iwa/core/services/__init__.py +17 -0
- iwa/core/services/account.py +57 -0
- iwa/core/services/balance.py +113 -0
- iwa/core/services/plugin.py +88 -0
- iwa/core/services/safe.py +392 -0
- iwa/core/services/transaction.py +172 -0
- iwa/core/services/transfer/__init__.py +166 -0
- iwa/core/services/transfer/base.py +260 -0
- iwa/core/services/transfer/erc20.py +247 -0
- iwa/core/services/transfer/multisend.py +386 -0
- iwa/core/services/transfer/native.py +262 -0
- iwa/core/services/transfer/swap.py +326 -0
- iwa/core/settings.py +95 -0
- iwa/core/tables.py +60 -0
- iwa/core/test.py +27 -0
- iwa/core/tests/test_wallet.py +255 -0
- iwa/core/types.py +59 -0
- iwa/core/ui.py +99 -0
- iwa/core/utils.py +59 -0
- iwa/core/wallet.py +380 -0
- iwa/plugins/__init__.py +1 -0
- iwa/plugins/gnosis/__init__.py +5 -0
- iwa/plugins/gnosis/cow/__init__.py +6 -0
- iwa/plugins/gnosis/cow/quotes.py +148 -0
- iwa/plugins/gnosis/cow/swap.py +403 -0
- iwa/plugins/gnosis/cow/types.py +20 -0
- iwa/plugins/gnosis/cow_utils.py +44 -0
- iwa/plugins/gnosis/plugin.py +68 -0
- iwa/plugins/gnosis/safe.py +157 -0
- iwa/plugins/gnosis/tests/test_cow.py +227 -0
- iwa/plugins/gnosis/tests/test_safe.py +100 -0
- iwa/plugins/olas/__init__.py +5 -0
- iwa/plugins/olas/constants.py +106 -0
- iwa/plugins/olas/contracts/activity_checker.py +93 -0
- iwa/plugins/olas/contracts/base.py +10 -0
- iwa/plugins/olas/contracts/mech.py +49 -0
- iwa/plugins/olas/contracts/mech_marketplace.py +43 -0
- iwa/plugins/olas/contracts/service.py +215 -0
- iwa/plugins/olas/contracts/staking.py +403 -0
- iwa/plugins/olas/importer.py +736 -0
- iwa/plugins/olas/mech_reference.py +135 -0
- iwa/plugins/olas/models.py +110 -0
- iwa/plugins/olas/plugin.py +243 -0
- iwa/plugins/olas/scripts/test_full_mech_flow.py +259 -0
- iwa/plugins/olas/scripts/test_simple_lifecycle.py +74 -0
- iwa/plugins/olas/service_manager/__init__.py +60 -0
- iwa/plugins/olas/service_manager/base.py +113 -0
- iwa/plugins/olas/service_manager/drain.py +336 -0
- iwa/plugins/olas/service_manager/lifecycle.py +839 -0
- iwa/plugins/olas/service_manager/mech.py +322 -0
- iwa/plugins/olas/service_manager/staking.py +530 -0
- iwa/plugins/olas/tests/conftest.py +30 -0
- iwa/plugins/olas/tests/test_importer.py +128 -0
- iwa/plugins/olas/tests/test_importer_error_handling.py +349 -0
- iwa/plugins/olas/tests/test_mech_contracts.py +85 -0
- iwa/plugins/olas/tests/test_olas_contracts.py +249 -0
- iwa/plugins/olas/tests/test_olas_integration.py +561 -0
- iwa/plugins/olas/tests/test_olas_models.py +144 -0
- iwa/plugins/olas/tests/test_olas_view.py +258 -0
- iwa/plugins/olas/tests/test_olas_view_actions.py +137 -0
- iwa/plugins/olas/tests/test_olas_view_modals.py +120 -0
- iwa/plugins/olas/tests/test_plugin.py +70 -0
- iwa/plugins/olas/tests/test_plugin_full.py +212 -0
- iwa/plugins/olas/tests/test_service_lifecycle.py +150 -0
- iwa/plugins/olas/tests/test_service_manager.py +1065 -0
- iwa/plugins/olas/tests/test_service_manager_errors.py +208 -0
- iwa/plugins/olas/tests/test_service_manager_flows.py +497 -0
- iwa/plugins/olas/tests/test_service_manager_mech.py +135 -0
- iwa/plugins/olas/tests/test_service_manager_rewards.py +360 -0
- iwa/plugins/olas/tests/test_service_manager_validation.py +145 -0
- iwa/plugins/olas/tests/test_service_staking.py +342 -0
- iwa/plugins/olas/tests/test_staking_integration.py +269 -0
- iwa/plugins/olas/tests/test_staking_validation.py +109 -0
- iwa/plugins/olas/tui/__init__.py +1 -0
- iwa/plugins/olas/tui/olas_view.py +952 -0
- iwa/tools/check_profile.py +67 -0
- iwa/tools/release.py +111 -0
- iwa/tools/reset_env.py +111 -0
- iwa/tools/reset_tenderly.py +362 -0
- iwa/tools/restore_backup.py +82 -0
- iwa/tui/__init__.py +1 -0
- iwa/tui/app.py +174 -0
- iwa/tui/modals/__init__.py +5 -0
- iwa/tui/modals/base.py +406 -0
- iwa/tui/rpc.py +63 -0
- iwa/tui/screens/__init__.py +1 -0
- iwa/tui/screens/wallets.py +749 -0
- iwa/tui/tests/test_app.py +125 -0
- iwa/tui/tests/test_rpc.py +139 -0
- iwa/tui/tests/test_wallets_refactor.py +30 -0
- iwa/tui/tests/test_widgets.py +123 -0
- iwa/tui/widgets/__init__.py +5 -0
- iwa/tui/widgets/base.py +100 -0
- iwa/tui/workers.py +42 -0
- iwa/web/dependencies.py +76 -0
- iwa/web/models.py +76 -0
- iwa/web/routers/accounts.py +115 -0
- iwa/web/routers/olas/__init__.py +24 -0
- iwa/web/routers/olas/admin.py +169 -0
- iwa/web/routers/olas/funding.py +135 -0
- iwa/web/routers/olas/general.py +29 -0
- iwa/web/routers/olas/services.py +378 -0
- iwa/web/routers/olas/staking.py +341 -0
- iwa/web/routers/state.py +65 -0
- iwa/web/routers/swap.py +617 -0
- iwa/web/routers/transactions.py +153 -0
- iwa/web/server.py +155 -0
- iwa/web/tests/test_web_endpoints.py +713 -0
- iwa/web/tests/test_web_olas.py +430 -0
- iwa/web/tests/test_web_swap.py +103 -0
- iwa-0.0.1a2.dist-info/METADATA +234 -0
- iwa-0.0.1a2.dist-info/RECORD +186 -0
- iwa-0.0.1a2.dist-info/entry_points.txt +2 -0
- iwa-0.0.1a2.dist-info/licenses/LICENSE +21 -0
- iwa-0.0.1a2.dist-info/top_level.txt +4 -0
- tests/legacy_cow.py +248 -0
- tests/legacy_safe.py +93 -0
- tests/legacy_transaction_retry_logic.py +51 -0
- tests/legacy_tui.py +440 -0
- tests/legacy_wallets_screen.py +554 -0
- tests/legacy_web.py +243 -0
- tests/test_account_service.py +120 -0
- tests/test_balance_service.py +186 -0
- tests/test_chain.py +490 -0
- tests/test_chain_interface.py +210 -0
- tests/test_cli.py +139 -0
- tests/test_contract.py +195 -0
- tests/test_db.py +180 -0
- tests/test_drain_coverage.py +174 -0
- tests/test_erc20.py +95 -0
- tests/test_gnosis_plugin.py +111 -0
- tests/test_keys.py +449 -0
- tests/test_legacy_wallet.py +1285 -0
- tests/test_main.py +13 -0
- tests/test_mnemonic.py +217 -0
- tests/test_modals.py +109 -0
- tests/test_models.py +213 -0
- tests/test_monitor.py +202 -0
- tests/test_multisend.py +84 -0
- tests/test_plugin_service.py +119 -0
- tests/test_pricing.py +143 -0
- tests/test_rate_limiter.py +199 -0
- tests/test_reset_tenderly.py +202 -0
- tests/test_rpc_view.py +73 -0
- tests/test_safe_coverage.py +139 -0
- tests/test_safe_service.py +168 -0
- tests/test_service_manager_integration.py +61 -0
- tests/test_service_manager_structure.py +31 -0
- tests/test_service_transaction.py +176 -0
- tests/test_staking_router.py +71 -0
- tests/test_staking_simple.py +31 -0
- tests/test_tables.py +76 -0
- tests/test_transaction_service.py +161 -0
- tests/test_transfer_multisend.py +179 -0
- tests/test_transfer_native.py +220 -0
- tests/test_transfer_security.py +93 -0
- tests/test_transfer_structure.py +37 -0
- tests/test_transfer_swap_unit.py +155 -0
- tests/test_ui_coverage.py +66 -0
- tests/test_utils.py +53 -0
- tests/test_workers.py +91 -0
- tools/verify_drain.py +183 -0
- __init__.py +0 -2
- hello.py +0 -6
- iwa-0.0.0.dist-info/METADATA +0 -10
- iwa-0.0.0.dist-info/RECORD +0 -6
- iwa-0.0.0.dist-info/top_level.txt +0 -2
- {iwa-0.0.0.dist-info → iwa-0.0.1a2.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
"""Tests for Olas service importer error handling and validation."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from unittest.mock import MagicMock, patch
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from iwa.plugins.olas.importer import DiscoveredKey, DiscoveredService, OlasServiceImporter
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.fixture
|
|
13
|
+
def mock_wallet():
|
|
14
|
+
"""Mock wallet."""
|
|
15
|
+
wallet = MagicMock()
|
|
16
|
+
wallet.key_storage = MagicMock()
|
|
17
|
+
return wallet
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@pytest.fixture
|
|
21
|
+
def importer(mock_wallet):
|
|
22
|
+
"""Importer fixture."""
|
|
23
|
+
with patch("iwa.core.models.Config"):
|
|
24
|
+
return OlasServiceImporter(mock_wallet)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_parse_plaintext_key_file_corrupted_json(importer, tmp_path):
|
|
28
|
+
"""Test parsing a file that contains invalid JSON."""
|
|
29
|
+
p = tmp_path / "corrupted.json"
|
|
30
|
+
p.write_text("{invalid: json}")
|
|
31
|
+
|
|
32
|
+
# Method is private but we test it for coverage
|
|
33
|
+
result = importer._parse_plaintext_key_file(str(p))
|
|
34
|
+
assert result is None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_parse_plaintext_key_file_not_dict(importer, tmp_path):
|
|
38
|
+
"""Test parsing a file that is valid JSON but not a dict."""
|
|
39
|
+
p = tmp_path / "list.json"
|
|
40
|
+
p.write_text("[1, 2, 3]")
|
|
41
|
+
|
|
42
|
+
result = importer._parse_plaintext_key_file(str(p))
|
|
43
|
+
assert result is None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_decrypt_key_invalid_format(importer):
|
|
47
|
+
"""Test decrypting a key with invalid format."""
|
|
48
|
+
key = DiscoveredKey(address="0x1", is_encrypted=True)
|
|
49
|
+
# Since key.encrypted_keystore is None and it's not a valid hex, it should fail
|
|
50
|
+
assert importer.decrypt_key(key, "pwd") is False
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_scan_directory_with_unreadable_subdir(importer, tmp_path):
|
|
54
|
+
"""Test scanning a directory with permission errors (mocked)."""
|
|
55
|
+
# Simply ensure it doesn't crash if walk returns something
|
|
56
|
+
with patch("os.walk") as mock_walk:
|
|
57
|
+
mock_walk.return_value = [
|
|
58
|
+
(str(tmp_path), ["subdir"], []),
|
|
59
|
+
]
|
|
60
|
+
results = importer.scan_directory(str(tmp_path))
|
|
61
|
+
assert len(results) == 0
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_import_service_missing_keys(importer):
|
|
65
|
+
"""Test importing a service with no keys."""
|
|
66
|
+
service = DiscoveredService(service_id=1, keys=[])
|
|
67
|
+
result = importer.import_service(service)
|
|
68
|
+
assert result.success is True # Empty import is technically successful?
|
|
69
|
+
# Actually let's check what it does.
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_import_service_already_exists(importer):
|
|
73
|
+
"""Test importing a service that is already in existing config."""
|
|
74
|
+
with patch("iwa.plugins.olas.models.OlasConfig") as mock_olas_config_cls:
|
|
75
|
+
mock_olas_config = mock_olas_config_cls.return_value
|
|
76
|
+
mock_olas_config.services = {"gnosis:1": MagicMock()}
|
|
77
|
+
# Inject the mock config into the importer's config
|
|
78
|
+
importer.config.plugins["olas"] = mock_olas_config
|
|
79
|
+
|
|
80
|
+
service = DiscoveredService(service_id=1, chain_name="gnosis")
|
|
81
|
+
result = importer.import_service(service)
|
|
82
|
+
# In current implementation, if _import_service_config fails with "duplicate",
|
|
83
|
+
# the service is added to 'skipped' and success remains True or False depending on other keys.
|
|
84
|
+
assert result.success is True
|
|
85
|
+
assert len(result.skipped) == 1
|
|
86
|
+
assert "already exists" in result.skipped[0] or "duplicate" in result.skipped[0]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_parse_plaintext_key_file_hex_but_invalid(importer, tmp_path):
|
|
90
|
+
"""Test parsing a file that looks like hex but isn't valid private key."""
|
|
91
|
+
p = tmp_path / "bad_hex.txt"
|
|
92
|
+
p.write_text("0xZZZZZZZZZZ") # Invalid hex
|
|
93
|
+
|
|
94
|
+
result = importer._parse_plaintext_key_file(str(p))
|
|
95
|
+
assert result is None
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_scan_operate_success(importer, tmp_path):
|
|
99
|
+
"""Test scanning a directory in .operate format."""
|
|
100
|
+
operate_dir = tmp_path / "service_dir" / ".operate"
|
|
101
|
+
operate_dir.mkdir(parents=True)
|
|
102
|
+
keys_dir = operate_dir / "keys"
|
|
103
|
+
keys_dir.mkdir()
|
|
104
|
+
(keys_dir / "0x123").write_text('{"address": "0x123", "crypto": {}}')
|
|
105
|
+
|
|
106
|
+
services_dir = operate_dir / "services"
|
|
107
|
+
services_dir.mkdir()
|
|
108
|
+
uuid_dir = services_dir / "some-uuid"
|
|
109
|
+
uuid_dir.mkdir()
|
|
110
|
+
(uuid_dir / "config.json").write_text(
|
|
111
|
+
'{"chain_configs": {"gnosis": {"chain_data": {"token": 42, "multisig": "0xSafe"}}}}'
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
results = importer.scan_directory(Path(tmp_path))
|
|
115
|
+
assert len(results) == 1
|
|
116
|
+
assert results[0].format == "operate"
|
|
117
|
+
assert results[0].service_id == 42
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def test_scan_operate_missing_keys(importer, tmp_path):
|
|
121
|
+
"""Test .operate directory with missing keys folder."""
|
|
122
|
+
operate_dir = Path(tmp_path) / ".operate"
|
|
123
|
+
operate_dir.mkdir()
|
|
124
|
+
# No keys dir
|
|
125
|
+
|
|
126
|
+
result = importer._parse_operate_format(operate_dir)
|
|
127
|
+
assert result == []
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def test_scan_operate_standalone_keys(importer, tmp_path):
|
|
131
|
+
"""Test .operate directory with standalone keys (no services)."""
|
|
132
|
+
operate_dir = Path(tmp_path) / "standalone.operate"
|
|
133
|
+
operate_dir.mkdir()
|
|
134
|
+
wallets_dir = operate_dir / "wallets"
|
|
135
|
+
wallets_dir.mkdir()
|
|
136
|
+
(wallets_dir / "ethereum.txt").write_text('{"address": "0x123", "private_key": "0xabc"}')
|
|
137
|
+
(wallets_dir / "ethereum.json").write_text('{"safes": {"gnosis": "0xSafe"}}')
|
|
138
|
+
|
|
139
|
+
services = importer._parse_operate_format(operate_dir)
|
|
140
|
+
assert len(services) == 1
|
|
141
|
+
assert services[0].safe_address == "0xSafe"
|
|
142
|
+
assert len(services[0].keys) == 1
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def test_parse_trader_runner_keys(importer, tmp_path):
|
|
146
|
+
"""Test _parse_trader_runner_format with agent and operator keys."""
|
|
147
|
+
runner_dir = Path(tmp_path) / ".trader_runner"
|
|
148
|
+
runner_dir.mkdir()
|
|
149
|
+
# Provision valid-ish keystore JSON (must have 'crypto' or 'ciphertext')
|
|
150
|
+
(runner_dir / "agent_pkey.txt").write_text(
|
|
151
|
+
'{"address": "0xAgent", "crypto": {"ciphertext": "abc"}}'
|
|
152
|
+
)
|
|
153
|
+
(runner_dir / "operator_pkey.txt").write_text(
|
|
154
|
+
'{"address": "0xOper", "crypto": {"ciphertext": "def"}}'
|
|
155
|
+
)
|
|
156
|
+
(runner_dir / "service_id.txt").write_text("100\n")
|
|
157
|
+
(runner_dir / "service_safe_address.txt").write_text("0xSafeAddress\n")
|
|
158
|
+
|
|
159
|
+
service = importer._parse_trader_runner_format(runner_dir)
|
|
160
|
+
assert service.service_id == 100
|
|
161
|
+
assert service.safe_address == "0xSafeAddress"
|
|
162
|
+
assert len(service.keys) == 2
|
|
163
|
+
assert any(k.role == "agent" for k in service.keys)
|
|
164
|
+
assert any(k.role == "operator" for k in service.keys)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def test_parse_trader_runner_invalid_id(importer, tmp_path):
|
|
168
|
+
"""Test _parse_trader_runner_format with invalid service_id."""
|
|
169
|
+
runner_dir = Path(tmp_path) / ".trader_runner"
|
|
170
|
+
runner_dir.mkdir()
|
|
171
|
+
(runner_dir / "service_id.txt").write_text("not-an-int\n")
|
|
172
|
+
(runner_dir / "agent_pkey.txt").write_text('{"address": "0x1", "crypto": {}}')
|
|
173
|
+
|
|
174
|
+
service = importer._parse_trader_runner_format(runner_dir)
|
|
175
|
+
assert service.service_id is None
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_parse_keys_json(importer, tmp_path):
|
|
179
|
+
"""Test _parse_keys_json with valid and invalid entries."""
|
|
180
|
+
keys_file = Path(tmp_path) / "keys.json"
|
|
181
|
+
keys_file.write_text(
|
|
182
|
+
json.dumps(
|
|
183
|
+
[
|
|
184
|
+
{"address": "0x1", "crypto": {"ciphertext": "a"}},
|
|
185
|
+
{"invalid": "key"},
|
|
186
|
+
{"address": "0x2", "crypto": {"ciphertext": "b"}},
|
|
187
|
+
]
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
keys = importer._parse_keys_json(keys_file)
|
|
192
|
+
assert len(keys) == 2
|
|
193
|
+
assert keys[0].address == "0x1"
|
|
194
|
+
assert keys[1].address == "0x2"
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def test_import_service_duplicate(importer):
|
|
198
|
+
"""Test importing a service that already exists in OlasConfig."""
|
|
199
|
+
from iwa.plugins.olas.importer import DiscoveredService
|
|
200
|
+
|
|
201
|
+
service = DiscoveredService(
|
|
202
|
+
service_id=1,
|
|
203
|
+
chain_name="gnosis",
|
|
204
|
+
source_folder=Path("/tmp"),
|
|
205
|
+
format="trader_runner",
|
|
206
|
+
service_name="existing",
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# Mock existing service in config
|
|
210
|
+
importer.config.plugins["olas"] = MagicMock()
|
|
211
|
+
importer.config.plugins["olas"].services = {"gnosis:1": MagicMock()}
|
|
212
|
+
|
|
213
|
+
success, msg = importer._import_service_config(service)
|
|
214
|
+
assert success is False
|
|
215
|
+
assert msg == "duplicate"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def test_import_key_duplicate(importer):
|
|
219
|
+
"""Test importing a key that already exists in KeyStorage."""
|
|
220
|
+
from iwa.plugins.olas.importer import DiscoveredKey
|
|
221
|
+
|
|
222
|
+
key = DiscoveredKey(
|
|
223
|
+
address="0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB",
|
|
224
|
+
private_key="0xabc",
|
|
225
|
+
role="agent",
|
|
226
|
+
source_file=Path("/tmp/key.txt"),
|
|
227
|
+
is_encrypted=False,
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
importer.key_storage.find_stored_account.return_value = MagicMock()
|
|
231
|
+
|
|
232
|
+
success, msg = importer._import_key(key, "service")
|
|
233
|
+
assert success is False
|
|
234
|
+
assert msg == "duplicate"
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def test_import_key_no_password(importer):
|
|
238
|
+
"""Test importing an encrypted key without providing a password."""
|
|
239
|
+
from iwa.plugins.olas.importer import DiscoveredKey
|
|
240
|
+
|
|
241
|
+
key = DiscoveredKey(
|
|
242
|
+
address="0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB",
|
|
243
|
+
private_key=None,
|
|
244
|
+
role="agent",
|
|
245
|
+
source_file=Path("/tmp/key.txt"),
|
|
246
|
+
is_encrypted=True,
|
|
247
|
+
encrypted_keystore={"crypto": {}},
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
importer.key_storage.find_stored_account.return_value = None
|
|
251
|
+
|
|
252
|
+
success, msg = importer._import_key(key, "service", password=None)
|
|
253
|
+
assert success is False
|
|
254
|
+
assert "password" in msg
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def test_generate_tag_collisions(importer):
|
|
258
|
+
"""Test tag generation with collisions."""
|
|
259
|
+
from iwa.plugins.olas.importer import DiscoveredKey
|
|
260
|
+
|
|
261
|
+
key = DiscoveredKey(address="0x1", private_key="0x1", role="agent", is_encrypted=False)
|
|
262
|
+
|
|
263
|
+
# Mock existing tags
|
|
264
|
+
importer.key_storage.accounts = {
|
|
265
|
+
"0x2": MagicMock(tag="test_service_agent"),
|
|
266
|
+
"0x3": MagicMock(tag="test_service_agent_2"),
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
tag = importer._generate_tag(key, "test_service")
|
|
270
|
+
assert tag == "test_service_agent_3"
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def test_import_safe_duplicate(importer):
|
|
274
|
+
"""Test importing a Safe that already exists."""
|
|
275
|
+
from iwa.plugins.olas.importer import DiscoveredService
|
|
276
|
+
|
|
277
|
+
service = DiscoveredService(
|
|
278
|
+
service_id=1, chain_name="gnosis", safe_address="0xSafe", source_folder=Path("/tmp")
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
importer.key_storage.find_stored_account.return_value = MagicMock()
|
|
282
|
+
|
|
283
|
+
success, msg = importer._import_safe(service)
|
|
284
|
+
assert success is False
|
|
285
|
+
assert msg == "duplicate"
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def test_import_key_success(importer):
|
|
289
|
+
"""Test successful key import with tag generation."""
|
|
290
|
+
from iwa.plugins.olas.importer import DiscoveredKey
|
|
291
|
+
|
|
292
|
+
addr = "0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB"
|
|
293
|
+
key = DiscoveredKey(
|
|
294
|
+
address=addr,
|
|
295
|
+
private_key="abc",
|
|
296
|
+
role="agent",
|
|
297
|
+
source_file=Path("/tmp/k"),
|
|
298
|
+
is_encrypted=False,
|
|
299
|
+
)
|
|
300
|
+
importer.key_storage.find_stored_account.return_value = None
|
|
301
|
+
importer.key_storage.accounts = {}
|
|
302
|
+
|
|
303
|
+
with patch("iwa.core.keys.EncryptedAccount.encrypt_private_key") as mock_enc:
|
|
304
|
+
mock_enc.return_value = MagicMock(address=addr)
|
|
305
|
+
success, msg = importer._import_key(key, "my_service")
|
|
306
|
+
assert success is True
|
|
307
|
+
assert msg == "ok"
|
|
308
|
+
assert addr in importer.key_storage.accounts
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
def test_import_safe_success(importer):
|
|
312
|
+
"""Test successful Safe import with tag generation."""
|
|
313
|
+
from iwa.plugins.olas.importer import DiscoveredService
|
|
314
|
+
|
|
315
|
+
service = DiscoveredService(
|
|
316
|
+
service_id=1,
|
|
317
|
+
chain_name="gnosis",
|
|
318
|
+
safe_address="0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB",
|
|
319
|
+
source_folder=Path("/tmp"),
|
|
320
|
+
service_name="my_service",
|
|
321
|
+
)
|
|
322
|
+
importer.key_storage.find_stored_account.return_value = None
|
|
323
|
+
importer.key_storage.accounts = {}
|
|
324
|
+
|
|
325
|
+
success, msg = importer._import_safe(service)
|
|
326
|
+
assert success is True
|
|
327
|
+
assert msg == "ok"
|
|
328
|
+
assert "0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB" in importer.key_storage.accounts
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def test_import_service_config_success(importer):
|
|
332
|
+
"""Test successful service config import."""
|
|
333
|
+
from iwa.plugins.olas.importer import DiscoveredService
|
|
334
|
+
|
|
335
|
+
service = DiscoveredService(
|
|
336
|
+
service_id=1,
|
|
337
|
+
chain_name="gnosis",
|
|
338
|
+
safe_address="0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB",
|
|
339
|
+
source_folder=Path("/tmp"),
|
|
340
|
+
service_name="my_service",
|
|
341
|
+
)
|
|
342
|
+
mock_olas = MagicMock()
|
|
343
|
+
mock_olas.services = {} # Use real dict
|
|
344
|
+
importer.config.plugins["olas"] = mock_olas
|
|
345
|
+
|
|
346
|
+
success, msg = importer._import_service_config(service)
|
|
347
|
+
assert success is True
|
|
348
|
+
assert msg == "ok"
|
|
349
|
+
mock_olas.add_service.assert_called_once()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Tests for Mech contracts."""
|
|
2
|
+
|
|
3
|
+
from unittest.mock import MagicMock, patch
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from iwa.plugins.olas.constants import PAYMENT_TYPE_NATIVE
|
|
8
|
+
from iwa.plugins.olas.contracts.mech import MechContract
|
|
9
|
+
from iwa.plugins.olas.contracts.mech_marketplace import MechMarketplaceContract
|
|
10
|
+
|
|
11
|
+
# Valid Ethereum addresses for testing
|
|
12
|
+
VALID_FROM_ADDRESS = "0x0000000000000000000000000000000000000001"
|
|
13
|
+
VALID_MECH_ADDRESS = "0x0000000000000000000000000000000000000002"
|
|
14
|
+
VALID_MARKETPLACE_ADDRESS = "0x0000000000000000000000000000000000000003"
|
|
15
|
+
VALID_PRIORITY_MECH = "0x0000000000000000000000000000000000000004"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class TestMechContracts:
|
|
19
|
+
"""Test suite for Mech contract classes."""
|
|
20
|
+
|
|
21
|
+
@pytest.fixture
|
|
22
|
+
def mock_chain_interface(self):
|
|
23
|
+
"""Mock chain interface."""
|
|
24
|
+
mock = MagicMock()
|
|
25
|
+
mock.chain_name = "gnosis"
|
|
26
|
+
return mock
|
|
27
|
+
|
|
28
|
+
def test_mech_contract_prepare_request_tx(self, mock_chain_interface):
|
|
29
|
+
"""Test prepare_request_tx for MechContract."""
|
|
30
|
+
with patch("iwa.core.contracts.contract.ChainInterfaces") as mock_interfaces_class:
|
|
31
|
+
mock_interfaces_class.return_value.get.return_value = mock_chain_interface
|
|
32
|
+
contract = MechContract(VALID_MECH_ADDRESS, "gnosis")
|
|
33
|
+
data = b"some data"
|
|
34
|
+
|
|
35
|
+
# Mocking prepare_transaction since it involves web3 objects
|
|
36
|
+
contract.prepare_transaction = MagicMock(
|
|
37
|
+
return_value={"data": "0xTxData", "value": 10**16}
|
|
38
|
+
)
|
|
39
|
+
# Mock get_price to avoid web3 call
|
|
40
|
+
contract.get_price = MagicMock(return_value=10**16)
|
|
41
|
+
|
|
42
|
+
tx = contract.prepare_request_tx(VALID_FROM_ADDRESS, data)
|
|
43
|
+
|
|
44
|
+
assert tx["data"] == "0xTxData"
|
|
45
|
+
contract.prepare_transaction.assert_called_once_with(
|
|
46
|
+
method_name="request",
|
|
47
|
+
method_kwargs={"data": data},
|
|
48
|
+
tx_params={"from": VALID_FROM_ADDRESS, "value": 10**16},
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def test_mech_marketplace_contract_prepare_request_tx(self, mock_chain_interface):
|
|
52
|
+
"""Test prepare_request_tx for MechMarketplaceContract."""
|
|
53
|
+
with patch("iwa.core.contracts.contract.ChainInterfaces") as mock_interfaces_class:
|
|
54
|
+
mock_interfaces_class.return_value.get.return_value = mock_chain_interface
|
|
55
|
+
contract = MechMarketplaceContract(VALID_MARKETPLACE_ADDRESS, "gnosis")
|
|
56
|
+
request_data = b"some data"
|
|
57
|
+
payment_type_bytes = bytes.fromhex(PAYMENT_TYPE_NATIVE)
|
|
58
|
+
|
|
59
|
+
contract.prepare_transaction = MagicMock(
|
|
60
|
+
return_value={"data": "0xMarketplaceTxData", "value": 10**16}
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
tx = contract.prepare_request_tx(
|
|
64
|
+
from_address=VALID_FROM_ADDRESS,
|
|
65
|
+
request_data=request_data,
|
|
66
|
+
priority_mech=VALID_PRIORITY_MECH,
|
|
67
|
+
response_timeout=300,
|
|
68
|
+
max_delivery_rate=10_000,
|
|
69
|
+
payment_type=payment_type_bytes,
|
|
70
|
+
payment_data=b"",
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
assert tx["data"] == "0xMarketplaceTxData"
|
|
74
|
+
contract.prepare_transaction.assert_called_once_with(
|
|
75
|
+
method_name="request",
|
|
76
|
+
method_kwargs={
|
|
77
|
+
"requestData": request_data,
|
|
78
|
+
"maxDeliveryRate": 10_000,
|
|
79
|
+
"paymentType": payment_type_bytes,
|
|
80
|
+
"priorityMech": VALID_PRIORITY_MECH,
|
|
81
|
+
"responseTimeout": 300,
|
|
82
|
+
"paymentData": b"",
|
|
83
|
+
},
|
|
84
|
+
tx_params={"from": VALID_FROM_ADDRESS, "value": 10**16},
|
|
85
|
+
)
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"""Tests for Olas contracts and ServiceManager advanced scenarios."""
|
|
2
|
+
|
|
3
|
+
from unittest.mock import MagicMock, patch
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from iwa.core.contracts.contract import ContractInstance
|
|
8
|
+
from iwa.plugins.olas.contracts.mech import MechContract
|
|
9
|
+
from iwa.plugins.olas.contracts.service import (
|
|
10
|
+
ServiceManagerContract,
|
|
11
|
+
ServiceRegistryContract,
|
|
12
|
+
ServiceState,
|
|
13
|
+
)
|
|
14
|
+
from iwa.plugins.olas.contracts.staking import StakingContract
|
|
15
|
+
from iwa.plugins.olas.mech_reference import MECH_ECOSYSTEM
|
|
16
|
+
from iwa.plugins.olas.models import Service
|
|
17
|
+
from iwa.plugins.olas.service_manager import ServiceManager
|
|
18
|
+
|
|
19
|
+
VALID_ADDR = "0x1234567890123456789012345678901234567890"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture
|
|
23
|
+
def mock_wallet():
|
|
24
|
+
"""Create a mock wallet fixture with necessary services."""
|
|
25
|
+
wallet = MagicMock(name="wallet_mock")
|
|
26
|
+
wallet.master_account.address = VALID_ADDR
|
|
27
|
+
wallet.key_storage = MagicMock(name="key_storage_mock")
|
|
28
|
+
wallet.transfer_service = MagicMock(name="transfer_service_mock")
|
|
29
|
+
wallet.account_service = MagicMock(name="account_service_mock")
|
|
30
|
+
wallet.account_service.get_accounts.return_value = []
|
|
31
|
+
wallet.safe_service = MagicMock(name="safe_service_mock")
|
|
32
|
+
wallet.safe_service.get_registry_address.return_value = VALID_ADDR
|
|
33
|
+
wallet.sign_and_send_transaction.return_value = (
|
|
34
|
+
True,
|
|
35
|
+
{"status": 1, "transactionHash": b"\x01" * 32},
|
|
36
|
+
)
|
|
37
|
+
return wallet
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def setup_manager(wallet):
|
|
41
|
+
"""Set up a ServiceManager instance with mocked dependencies."""
|
|
42
|
+
with patch("iwa.plugins.olas.service_manager.Config"):
|
|
43
|
+
with patch("iwa.plugins.olas.service_manager.ChainInterfaces") as mock_ci:
|
|
44
|
+
mock_ci.return_value.get.return_value = MagicMock()
|
|
45
|
+
mock_ci.return_value.get_contract_address.return_value = VALID_ADDR
|
|
46
|
+
with patch.object(ServiceManager, "_init_contracts"):
|
|
47
|
+
manager = ServiceManager(wallet)
|
|
48
|
+
manager.registry = MagicMock(name="registry_mock")
|
|
49
|
+
manager.manager = MagicMock(name="manager_mock")
|
|
50
|
+
manager.chain_name = "gnosis"
|
|
51
|
+
manager.wallet = wallet
|
|
52
|
+
return manager
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_staking_contract_properties(mock_wallet):
|
|
56
|
+
"""Test StakingContract properties and method integration."""
|
|
57
|
+
with patch("iwa.plugins.olas.contracts.staking.ActivityCheckerContract"):
|
|
58
|
+
with patch.object(ContractInstance, "call") as mock_call:
|
|
59
|
+
|
|
60
|
+
def side_effect(method, *args):
|
|
61
|
+
if method in [
|
|
62
|
+
"availableRewards",
|
|
63
|
+
"balance",
|
|
64
|
+
"livenessPeriod",
|
|
65
|
+
"rewardsPerSecond",
|
|
66
|
+
"maxNumServices",
|
|
67
|
+
"minStakingDeposit",
|
|
68
|
+
"minStakingDuration",
|
|
69
|
+
"epochCounter",
|
|
70
|
+
"getNextRewardCheckpointTimestamp",
|
|
71
|
+
"tsCheckpoint",
|
|
72
|
+
"getStakingState",
|
|
73
|
+
"calculateStakingReward",
|
|
74
|
+
]:
|
|
75
|
+
return 3600
|
|
76
|
+
if method == "getServiceIds":
|
|
77
|
+
return [1]
|
|
78
|
+
return VALID_ADDR
|
|
79
|
+
|
|
80
|
+
mock_call.side_effect = side_effect
|
|
81
|
+
c = StakingContract(VALID_ADDR, chain_name="gnosis")
|
|
82
|
+
c.calculate_accrued_staking_reward(1)
|
|
83
|
+
c.calculate_staking_reward(1)
|
|
84
|
+
c.get_epoch_counter()
|
|
85
|
+
c.get_next_epoch_start()
|
|
86
|
+
c.get_service_ids()
|
|
87
|
+
c.ts_checkpoint()
|
|
88
|
+
with patch("time.time", return_value=1000):
|
|
89
|
+
assert c.is_liveness_ratio_passed((1, 1), (0, 0), 1000) is False
|
|
90
|
+
with patch.object(c, "prepare_transaction", return_value={"ok": True}):
|
|
91
|
+
assert c.prepare_stake_tx(VALID_ADDR, 1) == {"ok": True}
|
|
92
|
+
assert c.prepare_unstake_tx(VALID_ADDR, 1) == {"ok": True}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@patch("iwa.plugins.olas.service_manager.ERC20Contract")
|
|
96
|
+
def test_service_manager_complex_registration(mock_erc20_cls, mock_wallet):
|
|
97
|
+
"""Test ServiceManager complex registration and deployment scenarios."""
|
|
98
|
+
manager = setup_manager(mock_wallet)
|
|
99
|
+
manager.service = Service(
|
|
100
|
+
service_name="t",
|
|
101
|
+
chain_name="gnosis",
|
|
102
|
+
service_id=1,
|
|
103
|
+
agent_ids=[1],
|
|
104
|
+
multisig_address=VALID_ADDR,
|
|
105
|
+
)
|
|
106
|
+
manager.service.token_address = VALID_ADDR
|
|
107
|
+
|
|
108
|
+
# register_agent successes
|
|
109
|
+
with patch(
|
|
110
|
+
"iwa.plugins.olas.service_manager.base.OLAS_CONTRACTS",
|
|
111
|
+
{"gnosis": {"OLAS_SERVICE_REGISTRY_TOKEN_UTILITY": VALID_ADDR}},
|
|
112
|
+
):
|
|
113
|
+
manager.registry.get_service.return_value = {
|
|
114
|
+
"state": ServiceState.ACTIVE_REGISTRATION,
|
|
115
|
+
"security_deposit": 50,
|
|
116
|
+
}
|
|
117
|
+
manager.registry.get_token.return_value = VALID_ADDR
|
|
118
|
+
manager.wallet.transfer_service.approve_erc20.return_value = True
|
|
119
|
+
manager.registry.extract_events.return_value = [{"name": "RegisterInstance"}]
|
|
120
|
+
mock_erc20_cls.return_value.balance_of_wei.return_value = 1000
|
|
121
|
+
assert manager.register_agent(VALID_ADDR, 100) is True
|
|
122
|
+
|
|
123
|
+
# deploy successes
|
|
124
|
+
manager.registry.get_service.return_value = {
|
|
125
|
+
"state": ServiceState.FINISHED_REGISTRATION,
|
|
126
|
+
"threshold": 1,
|
|
127
|
+
}
|
|
128
|
+
manager.registry.call.return_value = (None, [VALID_ADDR])
|
|
129
|
+
manager.registry.extract_events.return_value = [
|
|
130
|
+
{"name": "DeployService"},
|
|
131
|
+
{"name": "CreateMultisigWithAgents", "args": {"multisig": VALID_ADDR}},
|
|
132
|
+
]
|
|
133
|
+
assert manager.deploy() == VALID_ADDR
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def test_service_manager_initialization_failures(mock_wallet):
|
|
137
|
+
"""Test ServiceManager failure branches during initialization and setup."""
|
|
138
|
+
manager = setup_manager(mock_wallet)
|
|
139
|
+
manager.service = Service(service_name="t", chain_name="gnosis", service_id=1, agent_ids=[1])
|
|
140
|
+
|
|
141
|
+
# get_staking_status failures
|
|
142
|
+
manager.service = None
|
|
143
|
+
assert manager.get_staking_status() is None
|
|
144
|
+
|
|
145
|
+
manager.service = Service(service_name="t", chain_name="gnosis", service_id=1, agent_ids=[1])
|
|
146
|
+
# unbond status failure
|
|
147
|
+
manager.registry.get_service.return_value = {"state": ServiceState.FINISHED_REGISTRATION}
|
|
148
|
+
assert manager.unbond() is False
|
|
149
|
+
|
|
150
|
+
# sign failure in send_mech_request
|
|
151
|
+
mock_wallet.sign_and_send_transaction.return_value = (False, {})
|
|
152
|
+
with patch("iwa.plugins.olas.service_manager.MechContract") as mock_mech_cls:
|
|
153
|
+
mock_mech = mock_mech_cls.return_value
|
|
154
|
+
mock_mech.get_price.return_value = 10**15
|
|
155
|
+
mock_mech.prepare_request_tx.return_value = {"to": VALID_ADDR, "data": "0x"}
|
|
156
|
+
assert manager.send_mech_request(b"test", use_marketplace=False) is None
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def test_service_manager_config_edges(mock_wallet):
|
|
160
|
+
"""Test ServiceManager configuration and initialization edge cases."""
|
|
161
|
+
with patch("iwa.plugins.olas.service_manager.Config") as mock_cfg_cls:
|
|
162
|
+
mock_cfg = mock_cfg_cls.return_value
|
|
163
|
+
mock_cfg.plugins = {"olas": MagicMock()}
|
|
164
|
+
mock_cfg.plugins["olas"].get_service.return_value = Service(
|
|
165
|
+
service_name="t", chain_name="gnosis", service_id=1, agent_ids=[1]
|
|
166
|
+
)
|
|
167
|
+
with patch("iwa.plugins.olas.service_manager.ChainInterfaces"):
|
|
168
|
+
# hits 56
|
|
169
|
+
with patch(
|
|
170
|
+
"iwa.plugins.olas.service_manager.base.OLAS_CONTRACTS",
|
|
171
|
+
{
|
|
172
|
+
"gnosis": {
|
|
173
|
+
"OLAS_SERVICE_REGISTRY": VALID_ADDR,
|
|
174
|
+
"OLAS_SERVICE_MANAGER": VALID_ADDR,
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
):
|
|
178
|
+
with patch("iwa.plugins.olas.service_manager.ServiceRegistryContract"):
|
|
179
|
+
with patch("iwa.plugins.olas.service_manager.ServiceManagerContract"):
|
|
180
|
+
manager = ServiceManager(mock_wallet, service_key="gnosis:1")
|
|
181
|
+
assert manager.service is not None
|
|
182
|
+
|
|
183
|
+
# hits 78
|
|
184
|
+
with patch("iwa.plugins.olas.service_manager.base.OLAS_CONTRACTS", {"gnosis": {}}):
|
|
185
|
+
with pytest.raises(ValueError):
|
|
186
|
+
ServiceManager(mock_wallet)
|
|
187
|
+
|
|
188
|
+
# test mech reference export
|
|
189
|
+
assert "gnosis" in MECH_ECOSYSTEM
|
|
190
|
+
|
|
191
|
+
# test mech contract failures/edge cases
|
|
192
|
+
with patch.object(ContractInstance, "call") as mock_call:
|
|
193
|
+
mock_call.side_effect = Exception("price fail")
|
|
194
|
+
mech = MechContract(VALID_ADDR, chain_name="gnosis", use_new_abi=True)
|
|
195
|
+
assert mech.get_price() == 10**16
|
|
196
|
+
|
|
197
|
+
# test service registry token failure
|
|
198
|
+
reg = ServiceRegistryContract(VALID_ADDR, chain_name="gnosis")
|
|
199
|
+
with patch.object(ContractInstance, "call") as mock_call:
|
|
200
|
+
mock_call.side_effect = RuntimeError("token fail")
|
|
201
|
+
with pytest.raises(RuntimeError):
|
|
202
|
+
reg.get_token(1)
|
|
203
|
+
|
|
204
|
+
# test service manager deploy failure
|
|
205
|
+
mgr_contract = ServiceManagerContract(VALID_ADDR, chain_name="gnosis")
|
|
206
|
+
# Patch the chain_interface (which mgr_contract has) instead of ContractInstance
|
|
207
|
+
with patch.object(mgr_contract.chain_interface, "get_contract_address", return_value=None):
|
|
208
|
+
with pytest.raises(ValueError, match="Multisig implementation or fallback handler"):
|
|
209
|
+
mgr_contract.prepare_deploy_tx(VALID_ADDR, 1)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def test_service_manager_operation_failures(mock_wallet):
|
|
213
|
+
"""Test various ServiceManager operation failure paths."""
|
|
214
|
+
manager = setup_manager(mock_wallet)
|
|
215
|
+
manager.service = Service(service_name="t", chain_name="gnosis", service_id=1, agent_ids=[1])
|
|
216
|
+
|
|
217
|
+
# create failure - utility address missing
|
|
218
|
+
with patch("iwa.plugins.olas.service_manager.base.OLAS_CONTRACTS", {"gnosis": {}}):
|
|
219
|
+
manager.create("gnosis", "t") # it logs error but we just want the hit
|
|
220
|
+
|
|
221
|
+
# create failure - approve fails
|
|
222
|
+
with patch(
|
|
223
|
+
"iwa.plugins.olas.service_manager.base.OLAS_CONTRACTS",
|
|
224
|
+
{"gnosis": {"OLAS_SERVICE_REGISTRY_TOKEN_UTILITY": VALID_ADDR}},
|
|
225
|
+
):
|
|
226
|
+
mock_wallet.transfer_service.approve_erc20.return_value = False
|
|
227
|
+
manager.create("gnosis", "t", token_address_or_tag=VALID_ADDR)
|
|
228
|
+
|
|
229
|
+
# activate_registration - state mismatch
|
|
230
|
+
manager.registry.get_service.return_value = {"state": ServiceState.DEPLOYED}
|
|
231
|
+
assert manager.activate_registration() is False
|
|
232
|
+
|
|
233
|
+
# unbond - state mismatch
|
|
234
|
+
manager.registry.get_service.return_value = {"state": ServiceState.PRE_REGISTRATION}
|
|
235
|
+
assert manager.unbond() is False
|
|
236
|
+
|
|
237
|
+
# terminate - fail
|
|
238
|
+
manager.registry.get_service.return_value = {"state": ServiceState.DEPLOYED}
|
|
239
|
+
mock_wallet.sign_and_send_transaction.return_value = (False, {})
|
|
240
|
+
assert manager.terminate() is False
|
|
241
|
+
|
|
242
|
+
# unstake - service is None
|
|
243
|
+
manager.service = None
|
|
244
|
+
assert manager.unstake(VALID_ADDR) is False
|
|
245
|
+
|
|
246
|
+
# get() - service is None
|
|
247
|
+
assert manager.get() is None
|
|
248
|
+
|
|
249
|
+
# All tests passed
|