From ffebac1a7f7710e4c96a56945f518ee63e7b777c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Jul 2026 06:47:34 +0000 Subject: [PATCH 1/3] Initial plan From 27cc6b6b68af09a502f91da51f3187d8af2b08e3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Jul 2026 06:57:49 +0000 Subject: [PATCH 2/3] fix: az vm delete raises ResourceNotFoundError when VM or resource group does not exist --- .../azure/cli/command_modules/vm/commands.py | 3 +- .../cli/command_modules/vm/operations/vm.py | 19 +++++++- .../tests/latest/test_custom_vm_commands.py | 48 +++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/vm/commands.py b/src/azure-cli/azure/cli/command_modules/vm/commands.py index 0b127c6b260..5a494b19fa7 100644 --- a/src/azure-cli/azure/cli/command_modules/vm/commands.py +++ b/src/azure-cli/azure/cli/command_modules/vm/commands.py @@ -180,9 +180,10 @@ def load_command_table(self, _): g.generic_update_command('update', getter_name='get_vm_to_update_by_aaz', setter_name='update_vm', setter_type=compute_custom, command_type=compute_custom, supports_no_wait=True, validator=process_vm_update_namespace) g.wait_command('wait', getter_name='get_instance_view', getter_type=compute_custom) - from .operations.vm import VMCapture, VMDeallocate + from .operations.vm import VMCapture, VMDeallocate, VMDelete self.command_table['vm capture'] = VMCapture(loader=self) self.command_table['vm deallocate'] = VMDeallocate(loader=self) + self.command_table['vm delete'] = VMDelete(loader=self) with self.command_group('vm availability-set') as g: g.custom_command('create', 'create_av_set', table_transformer=deployment_validate_table_format, supports_no_wait=True, exception_handler=handle_template_based_exception) diff --git a/src/azure-cli/azure/cli/command_modules/vm/operations/vm.py b/src/azure-cli/azure/cli/command_modules/vm/operations/vm.py index 69d88f8faad..54d5c72d61b 100644 --- a/src/azure-cli/azure/cli/command_modules/vm/operations/vm.py +++ b/src/azure-cli/azure/cli/command_modules/vm/operations/vm.py @@ -10,7 +10,8 @@ from azure.cli.core.aaz import AAZStrType from ..aaz.latest.vm import (Show as _VMShow, ListSizes as _VMListSizes, Patch as _VMPatch, Update as _VMUpdate, Capture as _VMCapture, Create as _VMCreate, - ListUsage as _VMListUsage, Deallocate as _VMDeallocate) + ListUsage as _VMListUsage, Deallocate as _VMDeallocate, + Delete as _VMDelete) from .._vm_utils import IdentityType logger = get_logger(__name__) @@ -275,6 +276,22 @@ def _build_arguments_schema(cls, *args, **kwargs): return args_schema +class VMDelete(_VMDelete): + def pre_operations(self): + from azure.cli.core.azclierror import ResourceNotFoundError + resource_group = str(self.ctx.args.resource_group) + vm_name = str(self.ctx.args.name) + try: + VMShow(cli_ctx=self.cli_ctx)(command_args={ + 'resource_group': resource_group, + 'vm_name': vm_name + }) + except ResourceNotFoundError: + raise ResourceNotFoundError( + "The VM '{}' under resource group '{}' was not found.".format(vm_name, resource_group) + ) + + def convert_show_result_to_snake_case(result): new_result = {} if "id" in result: diff --git a/src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_custom_vm_commands.py b/src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_custom_vm_commands.py index 0291e22490b..0d646ba1b58 100644 --- a/src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_custom_vm_commands.py +++ b/src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_custom_vm_commands.py @@ -214,5 +214,53 @@ def __init__(self, is_linux, version): self.type_handler_version = version +class TestVMDeletePreOperations(unittest.TestCase): + """Unit tests for VMDelete.pre_operations existence check.""" + + @mock.patch('azure.cli.command_modules.vm.operations.vm.VMShow') + def test_pre_operations_raises_error_when_vm_not_found(self, mock_vm_show): + from azure.cli.core.azclierror import ResourceNotFoundError + from azure.cli.command_modules.vm.operations.vm import VMDelete + + # Mock VMShow to raise ResourceNotFoundError (simulating non-existent VM) + mock_vm_show_instance = mock.MagicMock() + mock_vm_show_instance.side_effect = ResourceNotFoundError("VM not found") + mock_vm_show.return_value = mock_vm_show_instance + + cli_ctx = DummyCli() + vm_delete = VMDelete(cli_ctx=cli_ctx) + + # Set up ctx with mocked args + vm_delete.ctx = mock.MagicMock() + vm_delete.ctx.args.resource_group = "nonexistent-rg" + vm_delete.ctx.args.name = "nonexistent-vm" + + with self.assertRaises(ResourceNotFoundError) as cm: + vm_delete.pre_operations() + + self.assertIn("nonexistent-vm", str(cm.exception)) + self.assertIn("nonexistent-rg", str(cm.exception)) + + @mock.patch('azure.cli.command_modules.vm.operations.vm.VMShow') + def test_pre_operations_succeeds_when_vm_exists(self, mock_vm_show): + from azure.cli.command_modules.vm.operations.vm import VMDelete + + # Mock VMShow to return successfully (simulating existing VM) + mock_vm_show_instance = mock.MagicMock() + mock_vm_show_instance.return_value = {'id': '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm1'} + mock_vm_show.return_value = mock_vm_show_instance + + cli_ctx = DummyCli() + vm_delete = VMDelete(cli_ctx=cli_ctx) + + # Set up ctx with mocked args + vm_delete.ctx = mock.MagicMock() + vm_delete.ctx.args.resource_group = "existing-rg" + vm_delete.ctx.args.name = "existing-vm" + + # Should not raise an exception + vm_delete.pre_operations() + + if __name__ == '__main__': unittest.main() From 4ff2faf768deaea7020e96eb78f1f5a6d5cc5ba3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 27 Jul 2026 08:13:35 +0000 Subject: [PATCH 3/3] fix: use on_204 override instead of pre_operations VMShow to avoid breaking VCR recordings --- .../cli/command_modules/vm/operations/vm.py | 17 +++---- .../tests/latest/test_custom_vm_commands.py | 45 +++++-------------- 2 files changed, 17 insertions(+), 45 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/vm/operations/vm.py b/src/azure-cli/azure/cli/command_modules/vm/operations/vm.py index 54d5c72d61b..58d2b20705d 100644 --- a/src/azure-cli/azure/cli/command_modules/vm/operations/vm.py +++ b/src/azure-cli/azure/cli/command_modules/vm/operations/vm.py @@ -277,18 +277,13 @@ def _build_arguments_schema(cls, *args, **kwargs): class VMDelete(_VMDelete): - def pre_operations(self): - from azure.cli.core.azclierror import ResourceNotFoundError - resource_group = str(self.ctx.args.resource_group) - vm_name = str(self.ctx.args.name) - try: - VMShow(cli_ctx=self.cli_ctx)(command_args={ - 'resource_group': resource_group, - 'vm_name': vm_name - }) - except ResourceNotFoundError: + class VirtualMachinesDelete(_VMDelete.VirtualMachinesDelete): + def on_204(self, session): + from azure.cli.core.azclierror import ResourceNotFoundError raise ResourceNotFoundError( - "The VM '{}' under resource group '{}' was not found.".format(vm_name, resource_group) + "The VM '{}' under resource group '{}' was not found.".format( + str(self.ctx.args.name), str(self.ctx.args.resource_group) + ) ) diff --git a/src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_custom_vm_commands.py b/src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_custom_vm_commands.py index 0d646ba1b58..7bdcab22c26 100644 --- a/src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_custom_vm_commands.py +++ b/src/azure-cli/azure/cli/command_modules/vm/tests/latest/test_custom_vm_commands.py @@ -214,53 +214,30 @@ def __init__(self, is_linux, version): self.type_handler_version = version -class TestVMDeletePreOperations(unittest.TestCase): - """Unit tests for VMDelete.pre_operations existence check.""" +class TestVMDeleteOn204(unittest.TestCase): + """Unit tests for VMDelete.VirtualMachinesDelete.on_204 behavior.""" - @mock.patch('azure.cli.command_modules.vm.operations.vm.VMShow') - def test_pre_operations_raises_error_when_vm_not_found(self, mock_vm_show): + def test_on_204_raises_resource_not_found_error(self): from azure.cli.core.azclierror import ResourceNotFoundError from azure.cli.command_modules.vm.operations.vm import VMDelete - # Mock VMShow to raise ResourceNotFoundError (simulating non-existent VM) - mock_vm_show_instance = mock.MagicMock() - mock_vm_show_instance.side_effect = ResourceNotFoundError("VM not found") - mock_vm_show.return_value = mock_vm_show_instance - cli_ctx = DummyCli() vm_delete = VMDelete(cli_ctx=cli_ctx) - # Set up ctx with mocked args - vm_delete.ctx = mock.MagicMock() - vm_delete.ctx.args.resource_group = "nonexistent-rg" - vm_delete.ctx.args.name = "nonexistent-vm" + # Simulate the ctx that would be set during command execution + ctx = mock.MagicMock() + ctx.args.name = "nonexistent-vm" + ctx.args.resource_group = "nonexistent-rg" + + # Instantiate the inner operation class with the mocked ctx + vm_delete_op = VMDelete.VirtualMachinesDelete(ctx=ctx) with self.assertRaises(ResourceNotFoundError) as cm: - vm_delete.pre_operations() + vm_delete_op.on_204(session=mock.MagicMock()) self.assertIn("nonexistent-vm", str(cm.exception)) self.assertIn("nonexistent-rg", str(cm.exception)) - @mock.patch('azure.cli.command_modules.vm.operations.vm.VMShow') - def test_pre_operations_succeeds_when_vm_exists(self, mock_vm_show): - from azure.cli.command_modules.vm.operations.vm import VMDelete - - # Mock VMShow to return successfully (simulating existing VM) - mock_vm_show_instance = mock.MagicMock() - mock_vm_show_instance.return_value = {'id': '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Compute/virtualMachines/vm1'} - mock_vm_show.return_value = mock_vm_show_instance - - cli_ctx = DummyCli() - vm_delete = VMDelete(cli_ctx=cli_ctx) - - # Set up ctx with mocked args - vm_delete.ctx = mock.MagicMock() - vm_delete.ctx.args.resource_group = "existing-rg" - vm_delete.ctx.args.name = "existing-vm" - - # Should not raise an exception - vm_delete.pre_operations() - if __name__ == '__main__': unittest.main()