diff --git a/gittensor/cli/issue_commands/mutations.py b/gittensor/cli/issue_commands/mutations.py index e63e2fb6..5536f618 100644 --- a/gittensor/cli/issue_commands/mutations.py +++ b/gittensor/cli/issue_commands/mutations.py @@ -200,8 +200,8 @@ def issue_register( / 'issue_bounty_manager.contract' ) if not contract_metadata.exists(): - console.print(f'[red]Error: Contract metadata not found at {contract_metadata}[/red]') - return + print_error(f'Contract metadata not found at {contract_metadata}') + raise SystemExit(1) contract_instance = ContractInstance.create_from_address( contract_address=contract_addr, @@ -238,7 +238,7 @@ def issue_register( print_error(str(error_info)) console.print(f'[cyan]Transaction Hash:[/cyan] {result.extrinsic_hash}') - return + raise SystemExit(1) print_success('Issue registered successfully!') console.print(f'[cyan]Transaction Hash:[/cyan] {result.extrinsic_hash}') diff --git a/gittensor/cli/issue_commands/view.py b/gittensor/cli/issue_commands/view.py index b6bc17d3..954527e5 100644 --- a/gittensor/cli/issue_commands/view.py +++ b/gittensor/cli/issue_commands/view.py @@ -322,5 +322,6 @@ def admin_info(network: str, rpc_url: str, contract: str, verbose: bool, as_json handle_exception(as_json=as_json, message=msg, error_type='read_failed') console.print(f'[yellow]{msg}[/yellow]') console.print('[dim]Try running with --verbose to see debug details.[/dim]') + raise SystemExit(1) except Exception as e: handle_exception(as_json=as_json, message=str(e)) diff --git a/tests/cli/test_cli_helpers.py b/tests/cli/test_cli_helpers.py index 8790f08f..9aabab59 100644 --- a/tests/cli/test_cli_helpers.py +++ b/tests/cli/test_cli_helpers.py @@ -872,6 +872,33 @@ def test_harvest_missing_contract_fails(self, cli_root, runner): assert 'Contract address not configured' in result.output +class TestCliRegisterLogicalFailures: + """Ensure logical failure branches in `issues register` exit non-zero.""" + + def test_register_exits_non_zero_when_contract_metadata_missing(self, cli_root, runner): + with ( + patch( + 'gittensor.cli.issue_commands.mutations._resolve_contract_and_network', + return_value=( + '0x1234567890123456789012345678901234567890', + 'wss://entrypoint-finney.opentensor.ai:443', + 'finney', + ), + ), + patch('gittensor.cli.issue_commands.mutations.validate_repository', return_value=('owner', 'repo')), + patch('gittensor.cli.issue_commands.mutations.validate_github_issue', return_value={}), + patch('substrateinterface.SubstrateInterface'), + patch('bittensor.Wallet'), + ): + result = runner.invoke( + cli_root, + ['issues', 'register', '--repo', 'owner/repo', '--issue', '1', '--bounty', '10', '-y'], + catch_exceptions=False, + ) + assert result.exit_code != 0 + assert 'Contract metadata not found' in result.output + + class TestCliRuntimeExceptions: """Ensure runtime/import failures exit non-zero for CLI commands.""" diff --git a/tests/cli/test_cli_json_error_output.py b/tests/cli/test_cli_json_error_output.py index 91aa8e5e..a1d6e765 100644 --- a/tests/cli/test_cli_json_error_output.py +++ b/tests/cli/test_cli_json_error_output.py @@ -66,3 +66,19 @@ def test_admin_info_emits_json_on_soft_read_failure(cli_root, runner): payload = json.loads(result.output) assert payload['success'] is False assert payload['error']['type'] == 'read_failed' + + +def test_admin_info_human_mode_exits_non_zero_on_soft_read_failure(cli_root, runner): + """`admin info` (human mode) must exit non-zero when packed storage read returns None.""" + with ( + patch( + 'gittensor.cli.issue_commands.view._resolve_contract_and_network', + return_value=('5Fakeaddr', 'ws://x', 'test'), + ), + patch('substrateinterface.SubstrateInterface', return_value=object()), + patch('gittensor.cli.issue_commands.view._read_contract_packed_storage', return_value=None), + ): + result = runner.invoke(cli_root, ['admin', 'info'], catch_exceptions=False) + + assert result.exit_code == 1 + assert 'Could not read contract configuration' in result.output