From ddb58d4ad8b89a381d1f09701e4505478f3c63ca Mon Sep 17 00:00:00 2001 From: cyc60 Date: Thu, 6 Aug 2026 11:44:36 +0300 Subject: [PATCH] Handle consolidation errors raised during manager creation --- src/validators/commands/consolidate.py | 10 +-- .../commands/tests/test_consolidate.py | 81 +++++++++++++++++++ 2 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 src/validators/commands/tests/test_consolidate.py diff --git a/src/validators/commands/consolidate.py b/src/validators/commands/consolidate.py index 86a2a8c4..b7902d03 100644 --- a/src/validators/commands/consolidate.py +++ b/src/validators/commands/consolidate.py @@ -333,12 +333,12 @@ async def process( source_public_keys=source_public_keys, target_public_key=target_public_key, ) - consolidation_manager = await ConsolidationManager.create( - consolidation_keys=consolidation_keys, - chain_head=chain_head, - exclude_public_keys=exclude_public_keys, - ) try: + consolidation_manager = await ConsolidationManager.create( + consolidation_keys=consolidation_keys, + chain_head=chain_head, + exclude_public_keys=exclude_public_keys, + ) target_source = consolidation_manager.get_target_source() except ConsolidationError as e: raise click.ClickException(str(e)) diff --git a/src/validators/commands/tests/test_consolidate.py b/src/validators/commands/tests/test_consolidate.py new file mode 100644 index 00000000..ed2ddfee --- /dev/null +++ b/src/validators/commands/tests/test_consolidate.py @@ -0,0 +1,81 @@ +from pathlib import Path +from typing import Generator +from unittest import mock + +import pytest +from click.testing import CliRunner + +from src.common.tests.factories import create_chain_head +from src.config.networks import HOODI +from src.validators.commands.consolidate import consolidate +from src.validators.exceptions import ConsolidationError + + +@pytest.fixture +def _patch_check_validators_manager() -> Generator: + with mock.patch( + 'src.validators.commands.consolidate.check_validators_manager', + return_value=None, + ): + yield + + +@pytest.fixture +def _patch_check_consolidations_queue() -> Generator: + with mock.patch( + 'src.validators.commands.consolidate._check_consolidations_queue', + return_value=None, + ): + yield + + +@pytest.fixture +def _patch_get_chain_latest_head() -> Generator: + with mock.patch( + 'src.validators.commands.consolidate.get_chain_latest_head', + return_value=create_chain_head(), + ): + yield + + +@pytest.mark.usefixtures( + '_patch_check_validators_manager', + '_patch_check_consolidations_queue', + '_patch_get_chain_latest_head', +) +class TestConsolidate: + @pytest.mark.usefixtures('fake_settings', 'setup_test_clients') + def test_create_consolidation_error_is_wrapped_in_click_exception( + self, + vault_address: str, + consensus_endpoints: str, + execution_endpoints: str, + data_dir: Path, + runner: CliRunner, + ): + """`ConsolidationManager.create` can raise `ConsolidationError` (e.g. an in-flight + pending consolidation with an unresolvable source balance); it must surface as a clean + `click.ClickException`, not fall through to the generic verbose-error handler.""" + args = [ + '--vault', + vault_address, + '--network', + HOODI, + '--consensus-endpoints', + consensus_endpoints, + '--execution-endpoints', + execution_endpoints, + '--data-dir', + str(data_dir), + '--no-confirm', + ] + with mock.patch( + 'src.validators.commands.consolidate.ConsolidationManager' + ) as consolidation_manager: + consolidation_manager.create = mock.AsyncMock( + side_effect=ConsolidationError('boom message') + ) + result = runner.invoke(consolidate, args) + + assert result.exit_code != 0 + assert 'Error: boom message' in result.output