Skip to content

Commit 01c2ccd

Browse files
committed
Add tests for mirage cleanup
1 parent 7aa9182 commit 01c2ccd

2 files changed

Lines changed: 213 additions & 2 deletions

File tree

node_cli/core/schains.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,8 @@ def cleanup_datadir_for_single_chain_node(
262262
folders = [f for f in os.listdir(base_path) if os.path.isdir(os.path.join(base_path, f))]
263263
if not folders:
264264
raise NoDataDirForChainError(
265-
f'No data directory found in {base_path}. Please check the path or specify a chain name.'
265+
f'No data directory found in {base_path}. '
266+
'Please check the path or specify a chain name.'
266267
)
267268
chain_name = folders[0]
268269
base_path = os.path.join(base_path, chain_name)

tests/mirage/mirage_node_test.py

Lines changed: 211 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
from unittest import mock
22

33
import freezegun
4+
import pytest
45

56
from node_cli.configs import SKALE_DIR
7+
from node_cli.configs.user import SKALE_DIR_ENV_FILEPATH
68
from node_cli.mirage.mirage_boot import init as init_boot
79
from node_cli.mirage.mirage_boot import update
8-
from node_cli.mirage.mirage_node import migrate_from_boot, request_repair, restore_mirage
10+
from node_cli.mirage.mirage_node import cleanup, migrate_from_boot, request_repair, restore_mirage
911
from node_cli.operations.mirage import MirageUpdateType
1012
from node_cli.utils.node_type import NodeType
1113
from tests.helper import CURRENT_DATETIME, CURRENT_TIMESTAMP
@@ -139,3 +141,211 @@ def test_mirage_repair(compose_node_env_mock, get_static_params_mock, redis_clie
139141
request_repair(snapshot_from='127.0.0.1')
140142
assert redis_client.get('test_repair_ts') == f'{CURRENT_TIMESTAMP}'.encode('utf-8')
141143
assert redis_client.get('test_snapshot_from') == b'127.0.0.1'
144+
145+
146+
@mock.patch('node_cli.utils.decorators.is_user_valid', return_value=True)
147+
@mock.patch('node_cli.mirage.mirage_node.cleanup_docker_configuration')
148+
@mock.patch('node_cli.mirage.mirage_node.cleanup_mirage_op')
149+
@mock.patch('node_cli.mirage.mirage_node.compose_node_env')
150+
def test_cleanup_success(
151+
mock_compose_env,
152+
mock_cleanup_mirage_op,
153+
mock_cleanup_docker_config,
154+
mock_is_user_valid,
155+
inited_node,
156+
resource_alloc,
157+
meta_file_v3,
158+
):
159+
mock_env = {'ENV_TYPE': 'devnet', 'SCHAIN_NAME': 'test_chain'}
160+
mock_compose_env.return_value = mock_env
161+
162+
cleanup()
163+
164+
mock_compose_env.assert_called_once_with(
165+
SKALE_DIR_ENV_FILEPATH, save=False, node_type=NodeType.MIRAGE
166+
)
167+
mock_cleanup_mirage_op.assert_called_once_with(mock_env, 'test_chain')
168+
mock_cleanup_docker_config.assert_called_once()
169+
170+
171+
@mock.patch('node_cli.utils.decorators.is_user_valid', return_value=True)
172+
@mock.patch('node_cli.mirage.mirage_node.cleanup_docker_configuration')
173+
@mock.patch('node_cli.mirage.mirage_node.cleanup_mirage_op')
174+
@mock.patch('node_cli.mirage.mirage_node.compose_node_env')
175+
def test_cleanup_calls_operations_in_correct_order(
176+
mock_compose_env,
177+
mock_cleanup_mirage_op,
178+
mock_cleanup_docker_config,
179+
mock_is_user_valid,
180+
inited_node,
181+
resource_alloc,
182+
meta_file_v3,
183+
):
184+
from node_cli.mirage.mirage_node import cleanup
185+
186+
mock_env = {'ENV_TYPE': 'devnet', 'SCHAIN_NAME': 'test_chain'}
187+
mock_compose_env.return_value = mock_env
188+
189+
# Create a mock manager to track call order
190+
manager = mock.Mock()
191+
manager.attach_mock(mock_compose_env, 'compose_env')
192+
manager.attach_mock(mock_cleanup_mirage_op, 'cleanup_mirage_op')
193+
manager.attach_mock(mock_cleanup_docker_config, 'cleanup_docker_config')
194+
195+
cleanup()
196+
197+
# Verify the order of calls
198+
expected_calls = [
199+
mock.call.compose_env(mock.ANY, save=False, node_type=mock.ANY),
200+
mock.call.cleanup_mirage_op(mock_env, 'test_chain'),
201+
mock.call.cleanup_docker_config(),
202+
]
203+
manager.assert_has_calls(expected_calls, any_order=False)
204+
205+
206+
@mock.patch('node_cli.utils.decorators.is_user_valid', return_value=True)
207+
@mock.patch('node_cli.mirage.mirage_node.cleanup_docker_configuration')
208+
@mock.patch('node_cli.mirage.mirage_node.cleanup_mirage_op')
209+
@mock.patch('node_cli.mirage.mirage_node.compose_node_env')
210+
def test_cleanup_with_different_chain_names(
211+
mock_compose_env,
212+
mock_cleanup_mirage_op,
213+
mock_cleanup_docker_config,
214+
mock_is_user_valid,
215+
inited_node,
216+
resource_alloc,
217+
meta_file_v3,
218+
):
219+
test_cases = [
220+
'simple_chain',
221+
'chain-with-hyphens',
222+
'chain_with_underscores',
223+
'ChainWithMixedCase',
224+
]
225+
226+
for chain_name in test_cases:
227+
mock_env = {'ENV_TYPE': 'devnet', 'SCHAIN_NAME': chain_name}
228+
mock_compose_env.return_value = mock_env
229+
230+
cleanup()
231+
232+
mock_cleanup_mirage_op.assert_called_with(mock_env, chain_name)
233+
234+
235+
@mock.patch('node_cli.utils.decorators.is_user_valid', return_value=True)
236+
@mock.patch('node_cli.mirage.mirage_node.cleanup_docker_configuration')
237+
@mock.patch(
238+
'node_cli.mirage.mirage_node.cleanup_mirage_op', side_effect=Exception('Cleanup failed')
239+
)
240+
@mock.patch('node_cli.mirage.mirage_node.compose_node_env')
241+
def test_cleanup_continues_after_mirage_op_error(
242+
mock_compose_env,
243+
mock_cleanup_mirage_op,
244+
mock_cleanup_docker_config,
245+
mock_is_user_valid,
246+
inited_node,
247+
resource_alloc,
248+
meta_file_v3,
249+
):
250+
mock_env = {'ENV_TYPE': 'devnet', 'SCHAIN_NAME': 'test_chain'}
251+
mock_compose_env.return_value = mock_env
252+
253+
# The function should raise the exception from cleanup_mirage_op
254+
with pytest.raises(Exception, match='Cleanup failed'):
255+
cleanup()
256+
257+
# But we should still verify the calls were made in order
258+
mock_compose_env.assert_called_once()
259+
mock_cleanup_mirage_op.assert_called_once_with(mock_env, 'test_chain')
260+
# cleanup_docker_configuration should not be called if cleanup_mirage_op fails
261+
mock_cleanup_docker_config.assert_not_called()
262+
263+
264+
@mock.patch('node_cli.utils.decorators.is_user_valid', return_value=False)
265+
def test_cleanup_fails_when_user_invalid(
266+
mock_is_user_valid,
267+
inited_node,
268+
resource_alloc,
269+
meta_file_v3,
270+
):
271+
"""Test that cleanup fails when user validation fails"""
272+
import pytest
273+
274+
from node_cli.mirage.mirage_node import cleanup
275+
276+
with pytest.raises(SystemExit):
277+
cleanup()
278+
279+
280+
def test_cleanup_fails_when_not_inited(ensure_meta_removed):
281+
import pytest
282+
283+
with pytest.raises(SystemExit):
284+
cleanup()
285+
286+
287+
@mock.patch('node_cli.utils.decorators.is_user_valid', return_value=True)
288+
@mock.patch('node_cli.mirage.mirage_node.cleanup_docker_configuration')
289+
@mock.patch('node_cli.mirage.mirage_node.cleanup_mirage_op')
290+
@mock.patch('node_cli.mirage.mirage_node.compose_node_env')
291+
@mock.patch('node_cli.mirage.mirage_node.logger')
292+
def test_cleanup_logs_success_message(
293+
mock_logger,
294+
mock_compose_env,
295+
mock_cleanup_mirage_op,
296+
mock_cleanup_docker_config,
297+
mock_is_user_valid,
298+
inited_node,
299+
resource_alloc,
300+
meta_file_v3,
301+
):
302+
mock_env = {'ENV_TYPE': 'devnet', 'SCHAIN_NAME': 'test_chain'}
303+
mock_compose_env.return_value = mock_env
304+
305+
cleanup()
306+
307+
mock_logger.info.assert_called_once_with(
308+
'Mirage node was cleaned up, all containers and data removed'
309+
)
310+
311+
312+
@mock.patch('node_cli.utils.decorators.is_user_valid', return_value=True)
313+
@mock.patch('node_cli.mirage.mirage_node.cleanup_docker_configuration')
314+
@mock.patch('node_cli.mirage.mirage_node.cleanup_mirage_op')
315+
@mock.patch('node_cli.mirage.mirage_node.compose_node_env')
316+
def test_cleanup_with_missing_schain_name(
317+
mock_compose_env,
318+
mock_cleanup_mirage_op,
319+
mock_cleanup_docker_config,
320+
mock_is_user_valid,
321+
inited_node,
322+
resource_alloc,
323+
meta_file_v3,
324+
):
325+
mock_env = {'ENV_TYPE': 'devnet'} # Missing SCHAIN_NAME
326+
mock_compose_env.return_value = mock_env
327+
328+
with pytest.raises(KeyError):
329+
cleanup()
330+
331+
332+
@mock.patch('node_cli.utils.decorators.is_user_valid', return_value=True)
333+
@mock.patch('node_cli.mirage.mirage_node.cleanup_docker_configuration')
334+
@mock.patch('node_cli.mirage.mirage_node.cleanup_mirage_op')
335+
@mock.patch('node_cli.mirage.mirage_node.compose_node_env')
336+
def test_cleanup_with_empty_schain_name(
337+
mock_compose_env,
338+
mock_cleanup_mirage_op,
339+
mock_cleanup_docker_config,
340+
mock_is_user_valid,
341+
inited_node,
342+
resource_alloc,
343+
meta_file_v3,
344+
):
345+
mock_env = {'ENV_TYPE': 'devnet', 'SCHAIN_NAME': ''}
346+
mock_compose_env.return_value = mock_env
347+
348+
cleanup()
349+
350+
mock_cleanup_mirage_op.assert_called_once_with(mock_env, '')
351+
mock_cleanup_docker_config.assert_called_once()

0 commit comments

Comments
 (0)