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
tests/legacy_cow.py
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
from unittest.mock import AsyncMock, MagicMock, patch
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# We need to mock cowdao_cowpy before importing CowSwap to avoid import errors
|
|
7
|
+
# if the library is not installed.
|
|
8
|
+
@pytest.fixture(autouse=True)
|
|
9
|
+
def mock_cowpy_modules():
|
|
10
|
+
"""Mock cowdao_cowpy modules and submodules globally for these tests."""
|
|
11
|
+
mock_cow = MagicMock()
|
|
12
|
+
mock_common = MagicMock()
|
|
13
|
+
mock_chains = MagicMock()
|
|
14
|
+
|
|
15
|
+
# Configure Chain enum mock
|
|
16
|
+
mock_chain_enum = MagicMock()
|
|
17
|
+
mock_chain_enum.value = [100] # Gnosis
|
|
18
|
+
mock_chains.Chain = [mock_chain_enum]
|
|
19
|
+
mock_chains.SupportedChainId = lambda x: x
|
|
20
|
+
|
|
21
|
+
modules = {
|
|
22
|
+
"cowdao_cowpy": MagicMock(),
|
|
23
|
+
"cowdao_cowpy.cow": mock_cow,
|
|
24
|
+
"cowdao_cowpy.cow.swap": MagicMock(),
|
|
25
|
+
"cowdao_cowpy.common": mock_common,
|
|
26
|
+
"cowdao_cowpy.common.chains": mock_chains,
|
|
27
|
+
"cowdao_cowpy.app_data": MagicMock(),
|
|
28
|
+
"cowdao_cowpy.app_data.utils": MagicMock(),
|
|
29
|
+
"cowdao_cowpy.contracts": MagicMock(),
|
|
30
|
+
"cowdao_cowpy.contracts.order": MagicMock(),
|
|
31
|
+
"cowdao_cowpy.contracts.sign": MagicMock(),
|
|
32
|
+
"cowdao_cowpy.order_book": MagicMock(),
|
|
33
|
+
"cowdao_cowpy.order_book.api": MagicMock(),
|
|
34
|
+
"cowdao_cowpy.order_book.config": MagicMock(),
|
|
35
|
+
"cowdao_cowpy.order_book.generated": MagicMock(),
|
|
36
|
+
"cowdao_cowpy.order_book.generated.model": MagicMock(),
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
with patch.dict("sys.modules", modules):
|
|
40
|
+
yield modules
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.fixture(autouse=True)
|
|
44
|
+
def clear_cowswap_cache():
|
|
45
|
+
"""Clear the lazy loading cache in CowSwap module."""
|
|
46
|
+
from iwa.plugins.gnosis.cow import _cowpy_cache
|
|
47
|
+
|
|
48
|
+
_cowpy_cache.clear()
|
|
49
|
+
yield
|
|
50
|
+
_cowpy_cache.clear()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
from iwa.core.chain import Gnosis
|
|
54
|
+
from iwa.plugins.gnosis.cow import CowSwap, OrderType
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@pytest.fixture
|
|
58
|
+
def mock_account():
|
|
59
|
+
with patch("iwa.plugins.gnosis.cow.Account") as mock:
|
|
60
|
+
mock.from_key.return_value = MagicMock(address="0xAccount", _address="0xAccount")
|
|
61
|
+
yield mock
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@pytest.fixture
|
|
65
|
+
def cow_swap(mock_account):
|
|
66
|
+
return CowSwap("private_key", Gnosis())
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@pytest.mark.asyncio
|
|
70
|
+
async def test_swap_sell(cow_swap):
|
|
71
|
+
with patch("iwa.plugins.gnosis.cow.swap_tokens", new_callable=AsyncMock) as mock_swap_tokens:
|
|
72
|
+
mock_swap_tokens.return_value = MagicMock(uid=MagicMock(root="order_uid"))
|
|
73
|
+
|
|
74
|
+
with patch.object(CowSwap, "check_cowswap_order", return_value=True):
|
|
75
|
+
success = await cow_swap.swap(1000, "OLAS", "WXDAI", order_type=OrderType.SELL)
|
|
76
|
+
assert success is True
|
|
77
|
+
mock_swap_tokens.assert_called_once()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@pytest.mark.asyncio
|
|
81
|
+
async def test_swap_buy(cow_swap):
|
|
82
|
+
with patch(
|
|
83
|
+
"iwa.plugins.gnosis.cow.CowSwap.swap_tokens_to_exact_tokens", new_callable=AsyncMock
|
|
84
|
+
) as mock_swap_tokens:
|
|
85
|
+
mock_swap_tokens.return_value = MagicMock(uid=MagicMock(root="order_uid"))
|
|
86
|
+
|
|
87
|
+
with patch.object(CowSwap, "check_cowswap_order", return_value=True):
|
|
88
|
+
success = await cow_swap.swap(1000, "OLAS", "WXDAI", order_type=OrderType.BUY)
|
|
89
|
+
assert success is True
|
|
90
|
+
mock_swap_tokens.assert_called_once()
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def test_check_cowswap_order_executed(cow_swap):
|
|
94
|
+
order = MagicMock()
|
|
95
|
+
order.url = "http://order.url"
|
|
96
|
+
|
|
97
|
+
with patch("iwa.plugins.gnosis.cow.requests.get") as mock_get:
|
|
98
|
+
mock_get.return_value.status_code = 200
|
|
99
|
+
mock_get.return_value.json.return_value = {
|
|
100
|
+
"status": "fulfilled",
|
|
101
|
+
"executedSellAmount": "100",
|
|
102
|
+
"executedBuyAmount": "100",
|
|
103
|
+
"quote": {"sellTokenPrice": 1.0, "buyTokenPrice": 1.0},
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
success = cow_swap.check_cowswap_order(order)
|
|
107
|
+
assert success is True
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def test_check_cowswap_order_expired(cow_swap):
|
|
111
|
+
order = MagicMock()
|
|
112
|
+
order.url = "http://order.url"
|
|
113
|
+
|
|
114
|
+
with patch("iwa.plugins.gnosis.cow.requests.get") as mock_get:
|
|
115
|
+
mock_get.return_value.status_code = 200
|
|
116
|
+
mock_get.return_value.json.return_value = {"status": "expired"}
|
|
117
|
+
|
|
118
|
+
success = cow_swap.check_cowswap_order(order)
|
|
119
|
+
assert success is False
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_get_chain_unsupported(cow_swap):
|
|
123
|
+
# Set to an ID that won't be found in our mock Chain list
|
|
124
|
+
cow_swap.supported_chain_id = 999
|
|
125
|
+
with pytest.raises(ValueError, match="Unsupported SupportedChainId"):
|
|
126
|
+
cow_swap.get_chain()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def test_check_cowswap_order_retries(cow_swap):
|
|
130
|
+
order = MagicMock()
|
|
131
|
+
order.url = "http://order.url"
|
|
132
|
+
|
|
133
|
+
with (
|
|
134
|
+
patch("iwa.plugins.gnosis.cow.requests.get") as mock_get,
|
|
135
|
+
patch("iwa.plugins.gnosis.cow.time.sleep") as mock_sleep,
|
|
136
|
+
):
|
|
137
|
+
mock_get.side_effect = [
|
|
138
|
+
MagicMock(status_code=404),
|
|
139
|
+
MagicMock(
|
|
140
|
+
status_code=200, json=lambda: {"status": "fulfilled", "executedSellAmount": "100"}
|
|
141
|
+
),
|
|
142
|
+
]
|
|
143
|
+
|
|
144
|
+
success = cow_swap.check_cowswap_order(order)
|
|
145
|
+
assert success is True
|
|
146
|
+
assert mock_get.call_count == 2
|
|
147
|
+
mock_sleep.assert_called_once()
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_check_cowswap_order_max_retries(cow_swap):
|
|
151
|
+
order = MagicMock()
|
|
152
|
+
order.url = "http://order.url"
|
|
153
|
+
|
|
154
|
+
with (
|
|
155
|
+
patch("iwa.plugins.gnosis.cow.requests.get") as mock_get,
|
|
156
|
+
patch("iwa.plugins.gnosis.cow.time.sleep"),
|
|
157
|
+
):
|
|
158
|
+
mock_get.return_value.status_code = 404
|
|
159
|
+
|
|
160
|
+
success = cow_swap.check_cowswap_order(order)
|
|
161
|
+
assert success is False
|
|
162
|
+
assert mock_get.call_count == 8
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@pytest.mark.asyncio
|
|
166
|
+
async def test_swap_exception(cow_swap):
|
|
167
|
+
with patch("iwa.plugins.gnosis.cow.swap_tokens", side_effect=Exception("Swap failed")):
|
|
168
|
+
success = await cow_swap.swap(1000, "OLAS", "WXDAI")
|
|
169
|
+
assert success is False
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@pytest.mark.asyncio
|
|
173
|
+
async def test_get_max_sell_amount_wei(cow_swap):
|
|
174
|
+
with patch("iwa.plugins.gnosis.cow.get_order_quote", new_callable=AsyncMock) as mock_get_quote:
|
|
175
|
+
mock_quote = MagicMock()
|
|
176
|
+
mock_quote.quote.sellAmount.root = "1000"
|
|
177
|
+
mock_get_quote.return_value = mock_quote
|
|
178
|
+
|
|
179
|
+
amount = await cow_swap.get_max_sell_amount_wei(1000, "0xSell", "0xBuy")
|
|
180
|
+
assert amount == int(1000 * 1.005)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@pytest.mark.asyncio
|
|
184
|
+
async def test_swap_tokens_to_exact_tokens(cow_swap):
|
|
185
|
+
with (
|
|
186
|
+
patch("iwa.plugins.gnosis.cow.get_order_quote", new_callable=AsyncMock) as mock_get_quote,
|
|
187
|
+
patch("iwa.plugins.gnosis.cow.post_order", new_callable=AsyncMock) as mock_post_order,
|
|
188
|
+
patch("iwa.plugins.gnosis.cow.sign_order"),
|
|
189
|
+
patch("iwa.plugins.gnosis.cow.CompletedOrder") as mock_completed_order,
|
|
190
|
+
):
|
|
191
|
+
mock_quote = MagicMock()
|
|
192
|
+
mock_quote.quote.sellAmount.root = "1000"
|
|
193
|
+
mock_quote.quote.validTo = 1234567890
|
|
194
|
+
mock_get_quote.return_value = mock_quote
|
|
195
|
+
|
|
196
|
+
mock_post_order.return_value = MagicMock(root="order_uid")
|
|
197
|
+
|
|
198
|
+
cow_swap.order_book_api = MagicMock()
|
|
199
|
+
cow_swap.order_book_api.get_order_link.return_value = "http://order.link"
|
|
200
|
+
|
|
201
|
+
mock_order_instance = MagicMock()
|
|
202
|
+
mock_order_instance.uid = MagicMock(root="order_uid")
|
|
203
|
+
mock_order_instance.url = "http://order.link"
|
|
204
|
+
mock_completed_order.return_value = mock_order_instance
|
|
205
|
+
|
|
206
|
+
order = await CowSwap.swap_tokens_to_exact_tokens(
|
|
207
|
+
amount=1000,
|
|
208
|
+
account=MagicMock(address="0xAccount", _address="0xAccount"),
|
|
209
|
+
chain=MagicMock(value=[100]),
|
|
210
|
+
sell_token="0xSell",
|
|
211
|
+
buy_token="0xBuy",
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
assert order.uid.root == "order_uid"
|
|
215
|
+
assert order.url == "http://order.link"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def test_check_cowswap_order_pending(cow_swap):
|
|
219
|
+
order = MagicMock()
|
|
220
|
+
order.url = "http://order.url"
|
|
221
|
+
|
|
222
|
+
with (
|
|
223
|
+
patch("iwa.plugins.gnosis.cow.requests.get") as mock_get,
|
|
224
|
+
patch("iwa.plugins.gnosis.cow.time.sleep") as mock_sleep,
|
|
225
|
+
):
|
|
226
|
+
mock_get.side_effect = [
|
|
227
|
+
MagicMock(
|
|
228
|
+
status_code=200,
|
|
229
|
+
json=lambda: {
|
|
230
|
+
"status": "open",
|
|
231
|
+
"executedSellAmount": "0",
|
|
232
|
+
"executedBuyAmount": "0",
|
|
233
|
+
},
|
|
234
|
+
),
|
|
235
|
+
MagicMock(
|
|
236
|
+
status_code=200,
|
|
237
|
+
json=lambda: {
|
|
238
|
+
"status": "fulfilled",
|
|
239
|
+
"executedSellAmount": "100",
|
|
240
|
+
"executedBuyAmount": "0",
|
|
241
|
+
},
|
|
242
|
+
),
|
|
243
|
+
]
|
|
244
|
+
|
|
245
|
+
success = cow_swap.check_cowswap_order(order)
|
|
246
|
+
assert success is True
|
|
247
|
+
assert mock_get.call_count == 2
|
|
248
|
+
mock_sleep.assert_called_once()
|
tests/legacy_safe.py
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from unittest.mock import MagicMock, patch
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
# Mock safe_eth before importing SafeMultisig
|
|
7
|
+
sys.modules["safe_eth"] = MagicMock()
|
|
8
|
+
sys.modules["safe_eth.eth"] = MagicMock()
|
|
9
|
+
sys.modules["safe_eth.eth.constants"] = MagicMock()
|
|
10
|
+
sys.modules["safe_eth.safe"] = MagicMock()
|
|
11
|
+
|
|
12
|
+
from iwa.core.models import StoredSafeAccount
|
|
13
|
+
from iwa.plugins.gnosis.safe import SafeMultisig
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.fixture
|
|
17
|
+
def mock_secrets():
|
|
18
|
+
with patch("iwa.plugins.gnosis.safe.settings") as mock:
|
|
19
|
+
mock.gnosis_rpc.get_secret_value.return_value = "http://rpc"
|
|
20
|
+
yield mock
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.fixture
|
|
24
|
+
def mock_safe_account():
|
|
25
|
+
account = MagicMock(spec=StoredSafeAccount)
|
|
26
|
+
account.address = "0xSafe"
|
|
27
|
+
account.chains = ["gnosis"]
|
|
28
|
+
return account
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.fixture
|
|
32
|
+
def mock_safe_lib():
|
|
33
|
+
with (
|
|
34
|
+
patch("iwa.plugins.gnosis.safe.Safe") as mock_safe,
|
|
35
|
+
patch("iwa.plugins.gnosis.safe.EthereumClient") as mock_client,
|
|
36
|
+
):
|
|
37
|
+
yield mock_safe, mock_client
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_init_success(mock_safe_account, mock_secrets, mock_safe_lib):
|
|
41
|
+
safe = SafeMultisig(mock_safe_account, "gnosis")
|
|
42
|
+
assert safe.multisig is not None
|
|
43
|
+
mock_safe_lib[1].assert_called_with("http://rpc") # EthereumClient
|
|
44
|
+
mock_safe_lib[0].assert_called_with("0xSafe", mock_safe_lib[1].return_value) # Safe
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_init_invalid_chain(mock_safe_account, mock_secrets):
|
|
48
|
+
with pytest.raises(ValueError, match="Safe account is not deployed on chain"):
|
|
49
|
+
SafeMultisig(mock_safe_account, "ethereum")
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_get_owners(mock_safe_account, mock_secrets, mock_safe_lib):
|
|
53
|
+
safe = SafeMultisig(mock_safe_account, "gnosis")
|
|
54
|
+
safe.multisig.retrieve_owners.return_value = ["0xOwner"]
|
|
55
|
+
assert safe.get_owners() == ["0xOwner"]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_get_threshold(mock_safe_account, mock_secrets, mock_safe_lib):
|
|
59
|
+
safe = SafeMultisig(mock_safe_account, "gnosis")
|
|
60
|
+
safe.multisig.retrieve_threshold.return_value = 2
|
|
61
|
+
assert safe.get_threshold() == 2
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_get_nonce(mock_safe_account, mock_secrets, mock_safe_lib):
|
|
65
|
+
safe = SafeMultisig(mock_safe_account, "gnosis")
|
|
66
|
+
safe.multisig.retrieve_nonce.return_value = 5
|
|
67
|
+
assert safe.get_nonce() == 5
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_retrieve_all_info(mock_safe_account, mock_secrets, mock_safe_lib):
|
|
71
|
+
safe = SafeMultisig(mock_safe_account, "gnosis")
|
|
72
|
+
safe.multisig.retrieve_all_info.return_value = {"owners": []}
|
|
73
|
+
assert safe.retrieve_all_info() == {"owners": []}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_send_tx_with_callback(mock_safe_account, mock_secrets, mock_safe_lib):
|
|
77
|
+
"""Test the new send_tx method with callback."""
|
|
78
|
+
safe = SafeMultisig(mock_safe_account, "gnosis")
|
|
79
|
+
mock_tx = MagicMock()
|
|
80
|
+
safe.multisig.build_multisig_tx.return_value = mock_tx
|
|
81
|
+
|
|
82
|
+
def sign_callback(safe_tx):
|
|
83
|
+
return "0xCallbackHash"
|
|
84
|
+
|
|
85
|
+
result = safe.send_tx(
|
|
86
|
+
to="0xTo",
|
|
87
|
+
value=100,
|
|
88
|
+
sign_and_execute_callback=sign_callback,
|
|
89
|
+
data="0x1234",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
assert result == "0xCallbackHash"
|
|
93
|
+
safe.multisig.build_multisig_tx.assert_called()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from unittest.mock import MagicMock, patch
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from iwa.tui.app import IwaApp
|
|
6
|
+
from iwa.tui.modals import CreateEOAModal
|
|
7
|
+
from iwa.tui.screens.wallets import WalletsScreen
|
|
8
|
+
|
|
9
|
+
# --- TUI Modal Tests ---
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@pytest.mark.asyncio
|
|
13
|
+
async def test_create_eoa_modal():
|
|
14
|
+
modal = CreateEOAModal()
|
|
15
|
+
mock_event = MagicMock()
|
|
16
|
+
mock_event.button.id = "cancel"
|
|
17
|
+
with patch.object(modal, "dismiss") as mock_dismiss:
|
|
18
|
+
modal.on_button_pressed(mock_event)
|
|
19
|
+
mock_dismiss.assert_called()
|
|
20
|
+
mock_event.button.id = "create"
|
|
21
|
+
try:
|
|
22
|
+
modal.on_button_pressed(mock_event)
|
|
23
|
+
except Exception:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# --- WalletsScreen Filtering Tests ---
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@pytest.mark.asyncio
|
|
31
|
+
async def test_wallets_view_filtering():
|
|
32
|
+
with patch("iwa.core.db.db"):
|
|
33
|
+
app = IwaApp()
|
|
34
|
+
async with app.run_test() as _:
|
|
35
|
+
view = app.query_one(WalletsScreen)
|
|
36
|
+
|
|
37
|
+
with (
|
|
38
|
+
patch.object(view, "fetch_all_balances") as mock_fetch,
|
|
39
|
+
patch.object(view, "refresh_table_structure_and_data") as mock_refresh,
|
|
40
|
+
):
|
|
41
|
+
mock_chk_event = MagicMock()
|
|
42
|
+
mock_chk_event.checkbox.id = "cb_Token"
|
|
43
|
+
mock_chk_event.value = True
|
|
44
|
+
|
|
45
|
+
view.on_checkbox_changed(mock_chk_event)
|
|
46
|
+
|
|
47
|
+
mock_fetch.assert_called_with(view.active_chain, ["Token"])
|
|
48
|
+
|
|
49
|
+
mock_chk_event.value = False
|
|
50
|
+
view.on_checkbox_changed(mock_chk_event)
|
|
51
|
+
mock_refresh.assert_called()
|