diff --git a/bittensor_cli/cli.py b/bittensor_cli/cli.py index 16e68c254..2543599ac 100755 --- a/bittensor_cli/cli.py +++ b/bittensor_cli/cli.py @@ -9449,13 +9449,18 @@ def proxy_add( def proxy_remove( self, delegate: Annotated[ - str, + Optional[str], typer.Option( callback=is_valid_ss58_address_param, - prompt="Enter the SS58 address of the delegate to remove, e.g. 5dxds...", - help="The SS58 address of the delegate to remove", + prompt=False, + help="The SS58 address of the delegate to remove (required if --all is not used)", ), - ] = "", + ] = None, + all: bool = typer.Option( + False, + "--all", + help="Remove all proxies associated with this account", + ), network: Optional[list[str]] = Options.network, proxy_type: ProxyType = Options.proxy_type, delay: int = typer.Option(0, help="Delay, in number of blocks"), @@ -9487,10 +9492,10 @@ def proxy_remove( [green]$[/green] btcli proxy remove --all """ - # TODO should add a --all flag to call Proxy.remove_proxies ? logger.debug( "args:\n" f"delegate: {delegate}\n" + f"all: {all}\n" f"network: {network}\n" f"proxy_type: {proxy_type}\n" f"delay: {delay}\n" @@ -9498,30 +9503,89 @@ def proxy_remove( f"wait_for_inclusion: {wait_for_inclusion}\n" f"era: {period}\n" ) - self.verbosity_handler(quiet, verbose, json_output, prompt) - wallet = self.wallet_ask( - wallet_name=wallet_name, - wallet_path=wallet_path, - wallet_hotkey=wallet_hotkey, - ask_for=[WO.NAME, WO.PATH], - validate=WV.WALLET, - ) - return self._run_command( - proxy_commands.remove_proxy( - subtensor=self.initialize_chain(network), - wallet=wallet, - delegate=delegate, - proxy_type=proxy_type, - delay=delay, - prompt=prompt, - decline=decline, - quiet=quiet, - wait_for_inclusion=wait_for_inclusion, - wait_for_finalization=wait_for_finalization, - period=period, - json_output=json_output, + # Validate that either delegate is provided or --all is used + if not all and not delegate: + if not json_output: + print_error( + "Either --delegate must be provided or --all flag must be used." + ) + else: + json_console.print_json( + data={ + "success": False, + "message": "Either --delegate must be provided or --all flag must be used.", + "extrinsic_identifier": None, + } + ) + return + + # If --all is used, delegate and proxy_type/delay should not be required + if all: + if delegate: + if not json_output: + console.print( + "[yellow]Warning: --delegate is ignored when --all flag is used.[/yellow]" + ) + self.verbosity_handler(quiet, verbose, json_output, prompt) + wallet = self.wallet_ask( + wallet_name=wallet_name, + wallet_path=wallet_path, + wallet_hotkey=wallet_hotkey, + ask_for=[WO.NAME, WO.PATH], + validate=WV.WALLET, + ) + return self._run_command( + proxy_commands.remove_proxies( + subtensor=self.initialize_chain(network), + wallet=wallet, + prompt=prompt, + decline=decline, + quiet=quiet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + json_output=json_output, + ) + ) + else: + # Single proxy removal - delegate is required + if not delegate: + if not json_output: + print_error("--delegate is required when --all flag is not used.") + else: + json_console.print_json( + data={ + "success": False, + "message": "--delegate is required when --all flag is not used.", + "extrinsic_identifier": None, + } + ) + return + + self.verbosity_handler(quiet, verbose, json_output, prompt) + wallet = self.wallet_ask( + wallet_name=wallet_name, + wallet_path=wallet_path, + wallet_hotkey=wallet_hotkey, + ask_for=[WO.NAME, WO.PATH], + validate=WV.WALLET, + ) + return self._run_command( + proxy_commands.remove_proxy( + subtensor=self.initialize_chain(network), + wallet=wallet, + delegate=delegate, + proxy_type=proxy_type, + delay=delay, + prompt=prompt, + decline=decline, + quiet=quiet, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + json_output=json_output, + ) ) - ) def proxy_kill( self, diff --git a/bittensor_cli/src/commands/proxy.py b/bittensor_cli/src/commands/proxy.py index 8852fedf1..adb938ba3 100644 --- a/bittensor_cli/src/commands/proxy.py +++ b/bittensor_cli/src/commands/proxy.py @@ -271,7 +271,7 @@ async def remove_proxy( """ if prompt: if not confirm_action( - f"This will remove a proxy of type {proxy_type.value} for delegate {delegate}." + f"This will remove a proxy of type {proxy_type.value} for delegate {delegate}. " f"Do you want to proceed?", decline=decline, quiet=quiet, @@ -309,6 +309,57 @@ async def remove_proxy( ) +async def remove_proxies( + subtensor: "SubtensorInterface", + wallet: "Wallet", + prompt: bool, + decline: bool, + quiet: bool, + wait_for_inclusion: bool, + wait_for_finalization: bool, + period: int, + json_output: bool, +) -> None: + """ + Executes the remove_proxies call on the chain to remove all proxies for the account + """ + if prompt: + confirmation = Prompt.ask( + "[red]WARNING:[/red] This will remove ALL proxies associated with this account.\n" + "[red]All proxy relationships will be permanently lost.[/red]\n" + "To proceed, enter [red]REMOVE[/red]" + ) + if confirmation != "REMOVE": + print_error("Invalid input. Operation cancelled.") + return None + if not (ulw := unlock_key(wallet, print_out=not json_output)).success: + if not json_output: + print_error(ulw.message) + else: + json_console.print_json( + data={ + "success": ulw.success, + "message": ulw.message, + "extrinsic_identifier": None, + } + ) + return None + call = await subtensor.substrate.compose_call( + call_module="Proxy", + call_function="remove_proxies", + call_params={}, + ) + return await submit_proxy( + subtensor=subtensor, + wallet=wallet, + call=call, + wait_for_inclusion=wait_for_inclusion, + wait_for_finalization=wait_for_finalization, + period=period, + json_output=json_output, + ) + + async def add_proxy( subtensor: "SubtensorInterface", wallet: "Wallet", diff --git a/tests/e2e_tests/test_proxy.py b/tests/e2e_tests/test_proxy.py index e5e76724e..b89c564ee 100644 --- a/tests/e2e_tests/test_proxy.py +++ b/tests/e2e_tests/test_proxy.py @@ -684,3 +684,457 @@ def test_add_proxy(local_chain, wallet_setup): os.environ["BTCLI_PROXIES_PATH"] = "" if os.path.exists(testing_db_loc): os.remove(testing_db_loc) + + +def test_remove_all_proxies(local_chain, wallet_setup): + """ + Tests removing all proxies using the --all flag + + Steps: + 1. Add multiple proxies (Dave and Bob as proxies of Alice) + 2. Verify proxies are added + 3. Remove all proxies using --all flag + 4. Verify all proxies are removed + 5. Attempt to use one of the proxies (should fail) + """ + testing_db_loc = "/tmp/btcli-test.db" + os.environ["BTCLI_PROXIES_PATH"] = testing_db_loc + wallet_path_alice = "//Alice" + wallet_path_bob = "//Bob" + wallet_path_dave = "//Dave" + + # Create wallets for Alice, Bob, and Dave + keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( + wallet_path_alice + ) + keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup( + wallet_path_bob + ) + keypair_dave, wallet_dave, wallet_path_dave, exec_command_dave = wallet_setup( + wallet_path_dave + ) + proxy_type_1 = "Transfer" + proxy_type_2 = "Staking" + delay = 0 + + try: + # Add Dave as a proxy of Alice (Transfer type) + add_result_1 = exec_command_alice( + command="proxy", + sub_command="add", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + "default", + "--delegate", + wallet_dave.coldkeypub.ss58_address, + "--proxy-type", + proxy_type_1, + "--delay", + str(delay), + "--period", + "128", + "--no-prompt", + "--json-output", + ], + ) + add_result_1_output = json.loads(add_result_1.stdout) + assert add_result_1_output["success"] is True + assert ( + add_result_1_output["data"]["delegatee"] + == wallet_dave.coldkeypub.ss58_address + ) + assert add_result_1_output["data"]["proxy_type"] == proxy_type_1 + print("Proxy 1 (Dave - Transfer) added successfully") + + # Add Bob as a proxy of Alice (Staking type) + add_result_2 = exec_command_alice( + command="proxy", + sub_command="add", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + "default", + "--delegate", + wallet_bob.coldkeypub.ss58_address, + "--proxy-type", + proxy_type_2, + "--delay", + str(delay), + "--period", + "128", + "--no-prompt", + "--json-output", + ], + ) + add_result_2_output = json.loads(add_result_2.stdout) + assert add_result_2_output["success"] is True + assert ( + add_result_2_output["data"]["delegatee"] + == wallet_bob.coldkeypub.ss58_address + ) + assert add_result_2_output["data"]["proxy_type"] == proxy_type_2 + print("Proxy 2 (Bob - Staking) added successfully") + + # Remove all proxies using --all flag + # Note: proxy_type is still required by CLI even with --all due to prompt=True + # so we provide it with a default value to avoid prompting + remove_all_result = exec_command_alice( + command="proxy", + sub_command="remove", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + "default", + "--all", + "--proxy-type", + "Any", # Required to avoid prompt even with --all + "--period", + "128", + "--no-prompt", + "--json-output", + ], + ) + remove_all_result_output = json.loads(remove_all_result.stdout) + assert remove_all_result_output["success"] is True + assert remove_all_result_output["message"] == "" + assert isinstance(remove_all_result_output["extrinsic_identifier"], str) + print("All proxies removed successfully") + + # Verify proxies are removed by attempting to use one (should fail) + # Try to use Dave as proxy (should fail since proxy was removed) + amount_to_transfer_proxy = 100 + transfer_result_proxy = exec_command_dave( + command="wallet", + sub_command="transfer", + extra_args=[ + "--wallet-path", + wallet_path_dave, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + "default", + "--proxy", + wallet_alice.coldkeypub.ss58_address, + "--dest", + keypair_bob.ss58_address, + "--amount", + str(amount_to_transfer_proxy), + "--no-prompt", + "--json-output", + ], + ) + transfer_result_proxy_output = json.loads(transfer_result_proxy.stdout) + # Should fail because proxy was removed + assert transfer_result_proxy_output["success"] is False + print("Verified proxy removal - transfer via removed proxy failed as expected") + + finally: + os.environ["BTCLI_PROXIES_PATH"] = "" + if os.path.exists(testing_db_loc): + os.remove(testing_db_loc) + + +def test_remove_proxy_validation(local_chain, wallet_setup): + """ + Tests validation for proxy remove command + + Steps: + 1. Attempt to remove proxy without --delegate or --all (should fail) + 2. Attempt to remove with --all flag (should succeed if no proxies exist) + """ + testing_db_loc = "/tmp/btcli-test.db" + os.environ["BTCLI_PROXIES_PATH"] = testing_db_loc + wallet_path_alice = "//Alice" + + # Create wallet for Alice + keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( + wallet_path_alice + ) + + try: + # Attempt to remove proxy without --delegate or --all (should fail) + # Note: proxy_type must be provided to avoid Typer prompt, but validation + # should still fail because delegate is missing + remove_result = exec_command_alice( + command="proxy", + sub_command="remove", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + "default", + "--proxy-type", + "Any", # Required to avoid prompt, but delegate still missing + "--period", + "128", + "--no-prompt", + "--json-output", + ], + ) + remove_result_output = json.loads(remove_result.stdout) + assert remove_result_output["success"] is False + assert ( + "Either --delegate must be provided or --all flag must be used" + in remove_result_output["message"] + ) + print("Validation test passed - correctly rejected missing delegate/--all") + + # Attempt to remove all proxies when none exist (should succeed) + # Note: proxy_type is still required by CLI even with --all due to prompt=True + # so we provide it with a default value to avoid prompting + remove_all_result = exec_command_alice( + command="proxy", + sub_command="remove", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + "default", + "--all", + "--proxy-type", + "Any", # Required to avoid prompt even with --all + "--period", + "128", + "--no-prompt", + "--json-output", + ], + ) + remove_all_result_output = json.loads(remove_all_result.stdout) + # Should succeed even if no proxies exist (remove_proxies extrinsic handles this) + assert remove_all_result_output["success"] is True + print("Validation test passed - --all works even when no proxies exist") + + finally: + os.environ["BTCLI_PROXIES_PATH"] = "" + if os.path.exists(testing_db_loc): + os.remove(testing_db_loc) + + +def test_remove_proxy(local_chain, wallet_setup): + """ + Tests removing a single proxy (without --all flag) + + Steps: + 1. Add Dave as a proxy of Alice + 2. Verify proxy is added and can be used + 3. Remove the specific proxy using --delegate flag + 4. Verify proxy is removed by attempting to use it (should fail) + """ + testing_db_loc = "/tmp/btcli-test.db" + os.environ["BTCLI_PROXIES_PATH"] = testing_db_loc + wallet_path_alice = "//Alice" + wallet_path_bob = "//Bob" + wallet_path_dave = "//Dave" + + # Create wallets for Alice, Bob, and Dave + keypair_alice, wallet_alice, wallet_path_alice, exec_command_alice = wallet_setup( + wallet_path_alice + ) + keypair_bob, wallet_bob, wallet_path_bob, exec_command_bob = wallet_setup( + wallet_path_bob + ) + keypair_dave, wallet_dave, wallet_path_dave, exec_command_dave = wallet_setup( + wallet_path_dave + ) + proxy_type = "Transfer" + delay = 0 + + try: + # Add Dave as a proxy of Alice + add_result = exec_command_alice( + command="proxy", + sub_command="add", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + "default", + "--delegate", + wallet_dave.coldkeypub.ss58_address, + "--proxy-type", + proxy_type, + "--delay", + str(delay), + "--period", + "128", + "--no-prompt", + "--json-output", + ], + ) + add_result_output = json.loads(add_result.stdout) + assert add_result_output["success"] is True + assert ( + add_result_output["data"]["delegatee"] + == wallet_dave.coldkeypub.ss58_address + ) + assert ( + add_result_output["data"]["delegator"] + == wallet_alice.coldkeypub.ss58_address + ) + assert add_result_output["data"]["proxy_type"] == proxy_type + assert add_result_output["data"]["delay"] == delay + print("Proxy added successfully") + + # Verify proxy works by checking Bob's initial balance + balance_result = exec_command_bob( + command="wallet", + sub_command="balance", + extra_args=[ + "--wallet-path", + wallet_path_bob, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + "default", + "--json-output", + ], + ) + balance_result_output = json.loads(balance_result.stdout) + assert ( + balance_result_output["balances"]["default"]["coldkey"] + == wallet_bob.coldkeypub.ss58_address + ) + bob_init_balance = balance_result_output["balances"]["default"]["free"] + + # Check Alice's initial balance + balance_result = exec_command_alice( + command="wallet", + sub_command="balance", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + "default", + "--json-output", + ], + ) + balance_result_output = json.loads(balance_result.stdout) + assert ( + balance_result_output["balances"]["default"]["coldkey"] + == wallet_alice.coldkeypub.ss58_address + ) + alice_init_balance = balance_result_output["balances"]["default"]["free"] + + # Use the proxy to transfer from Alice to Bob + amount_to_transfer_proxy = 100 + transfer_result_proxy = exec_command_dave( + command="wallet", + sub_command="transfer", + extra_args=[ + "--wallet-path", + wallet_path_dave, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + "default", + "--proxy", + wallet_alice.coldkeypub.ss58_address, + "--dest", + keypair_bob.ss58_address, + "--amount", + str(amount_to_transfer_proxy), + "--no-prompt", + "--json-output", + ], + ) + transfer_result_proxy_output = json.loads(transfer_result_proxy.stdout) + assert transfer_result_proxy_output["success"] is True + print("Proxy transfer successful - proxy is working") + + # Verify Bob received the funds + balance_result = exec_command_bob( + command="wallet", + sub_command="balance", + extra_args=[ + "--wallet-path", + wallet_path_bob, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + "default", + "--json-output", + ], + ) + balance_result_output = json.loads(balance_result.stdout) + assert ( + balance_result_output["balances"]["default"]["free"] + == float(amount_to_transfer_proxy) + bob_init_balance + ) + + # Now remove the proxy using single proxy removal (without --all) + remove_result = exec_command_alice( + command="proxy", + sub_command="remove", + extra_args=[ + "--wallet-path", + wallet_path_alice, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + "default", + "--delegate", + wallet_dave.coldkeypub.ss58_address, + "--proxy-type", + proxy_type, + "--delay", + str(delay), + "--period", + "128", + "--no-prompt", + "--json-output", + ], + ) + remove_result_output = json.loads(remove_result.stdout) + assert remove_result_output["success"] is True + assert remove_result_output["message"] == "" + assert isinstance(remove_result_output["extrinsic_identifier"], str) + print("Single proxy removal successful") + + # Verify proxy is removed by attempting to use it (should fail) + transfer_result_proxy_2 = exec_command_dave( + command="wallet", + sub_command="transfer", + extra_args=[ + "--wallet-path", + wallet_path_dave, + "--chain", + "ws://127.0.0.1:9945", + "--wallet-name", + "default", + "--proxy", + wallet_alice.coldkeypub.ss58_address, + "--dest", + keypair_bob.ss58_address, + "--amount", + str(amount_to_transfer_proxy), + "--no-prompt", + "--json-output", + ], + ) + transfer_result_proxy_2_output = json.loads(transfer_result_proxy_2.stdout) + # Should fail because proxy was removed + assert transfer_result_proxy_2_output["success"] is False + print("Verified proxy removal - transfer via removed proxy failed as expected") + + finally: + os.environ["BTCLI_PROXIES_PATH"] = "" + if os.path.exists(testing_db_loc): + os.remove(testing_db_loc) diff --git a/tests/unit_tests/test_cli.py b/tests/unit_tests/test_cli.py index 60cc10708..0153f21c5 100644 --- a/tests/unit_tests/test_cli.py +++ b/tests/unit_tests/test_cli.py @@ -805,3 +805,184 @@ async def test_set_root_weights_skips_current_weights_without_prompt(): ) mock_get_current.assert_not_called() + + +# ============================================================================ +# Tests for proxy_remove command +# ============================================================================ + + +@patch("bittensor_cli.cli.print_error") +def test_proxy_remove_requires_delegate_or_all(mock_print_error): + """Test that proxy_remove requires either --delegate or --all flag""" + cli_manager = CLIManager() + + # Test without delegate and without --all (should fail) + cli_manager.proxy_remove( + delegate=None, + all=False, + network=None, + proxy_type="Transfer", + delay=0, + wallet_name="test_wallet", + wallet_path="/tmp/test", + wallet_hotkey="test_hotkey", + prompt=False, + decline=False, + wait_for_inclusion=False, + wait_for_finalization=False, + period=100, + quiet=True, + verbose=False, + json_output=False, + ) + + # Should print error message + mock_print_error.assert_called_once_with( + "Either --delegate must be provided or --all flag must be used." + ) + + +@patch("bittensor_cli.cli.json_console") +def test_proxy_remove_requires_delegate_or_all_json(mock_json_console): + """Test that proxy_remove requires either --delegate or --all flag (JSON output)""" + cli_manager = CLIManager() + + # Test without delegate and without --all (should fail with JSON output) + cli_manager.proxy_remove( + delegate=None, + all=False, + network=None, + proxy_type="Transfer", + delay=0, + wallet_name="test_wallet", + wallet_path="/tmp/test", + wallet_hotkey="test_hotkey", + prompt=False, + decline=False, + wait_for_inclusion=False, + wait_for_finalization=False, + period=100, + quiet=True, + verbose=False, + json_output=True, + ) + + # Should print JSON error + mock_json_console.print_json.assert_called_once() + call_args = mock_json_console.print_json.call_args[1]["data"] + assert call_args["success"] is False + assert ( + "Either --delegate must be provided or --all flag must be used." + in call_args["message"] + ) + assert call_args["extrinsic_identifier"] is None + + +@patch("bittensor_cli.cli.console") +@patch("bittensor_cli.cli.proxy_commands") +def test_proxy_remove_with_all_flag(mock_proxy_commands, mock_console): + """Test that proxy_remove with --all flag calls remove_proxies""" + cli_manager = CLIManager() + valid_ss58 = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" + mock_wallet = Mock() + mock_subtensor = Mock() + + with ( + patch.object(cli_manager, "verbosity_handler"), + patch.object(cli_manager, "wallet_ask", return_value=mock_wallet), + patch.object(cli_manager, "initialize_chain", return_value=mock_subtensor), + patch.object(cli_manager, "_run_command") as mock_run_command, + ): + cli_manager.proxy_remove( + delegate=valid_ss58, # Should be ignored when --all is True + all=True, + network=None, + proxy_type="Transfer", + delay=0, + wallet_name="test_wallet", + wallet_path="/tmp/test", + wallet_hotkey="test_hotkey", + prompt=False, + decline=False, + wait_for_inclusion=False, + wait_for_finalization=False, + period=100, + quiet=True, + verbose=False, + json_output=False, + ) + + # Should show warning that delegate is ignored + mock_console.print.assert_called_once_with( + "[yellow]Warning: --delegate is ignored when --all flag is used.[/yellow]" + ) + + # Should call remove_proxies (not remove_proxy) + mock_proxy_commands.remove_proxies.assert_called_once_with( + subtensor=mock_subtensor, + wallet=mock_wallet, + prompt=False, + decline=False, + quiet=True, + wait_for_inclusion=False, + wait_for_finalization=False, + period=100, + json_output=False, + ) + + # Should call _run_command with the result + mock_run_command.assert_called_once() + + +@patch("bittensor_cli.cli.proxy_commands") +def test_proxy_remove_with_delegate_calls_remove_proxy(mock_proxy_commands): + """Test that proxy_remove with --delegate calls remove_proxy with correct parameters""" + cli_manager = CLIManager() + valid_delegate = "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" + mock_wallet = Mock() + mock_subtensor = Mock() + + with ( + patch.object(cli_manager, "verbosity_handler"), + patch.object(cli_manager, "wallet_ask", return_value=mock_wallet), + patch.object(cli_manager, "initialize_chain", return_value=mock_subtensor), + patch.object(cli_manager, "_run_command") as mock_run_command, + ): + cli_manager.proxy_remove( + delegate=valid_delegate, + all=False, + network=None, + proxy_type="Transfer", + delay=10, + wallet_name="test_wallet", + wallet_path="/tmp/test", + wallet_hotkey="test_hotkey", + prompt=False, + decline=False, + wait_for_inclusion=True, + wait_for_finalization=True, + period=100, + quiet=True, + verbose=False, + json_output=False, + ) + + # Should call remove_proxy with correct parameters + mock_proxy_commands.remove_proxy.assert_called_once_with( + subtensor=mock_subtensor, + wallet=mock_wallet, + delegate=valid_delegate, + proxy_type="Transfer", + delay=10, + prompt=False, + decline=False, + quiet=True, + wait_for_inclusion=True, + wait_for_finalization=True, + period=100, + json_output=False, + ) + + # Should call _run_command with the result + mock_run_command.assert_called_once()