From 4a92e1fcc558c75cf268c895810b4c38ba37a4e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Jul 2026 03:27:02 +0000 Subject: [PATCH 01/11] Initial plan From 5837e1cc2020f85e09985c59cba8db1a0f3a7070 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Jul 2026 03:31:05 +0000 Subject: [PATCH 02/11] [AppService] Restore appServicePlanId in webapp output --- .../tests/latest/test_webapp_commands_thru_mock.py | 11 +++++++++++ .../azure/cli/command_modules/appservice/utils.py | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py index ec6a96e6502..38c0c5c21e0 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py @@ -11,6 +11,7 @@ from azure.core.exceptions import HttpResponseError from azure.mgmt.web import WebSiteManagementClient +from azure.mgmt.web.models import Site from knack.util import CLIError from azure.cli.core.azclierror import (InvalidArgumentValueError, MutuallyExclusiveArgumentError, @@ -40,6 +41,7 @@ list_startup_logs, show_startup_log, create_webapp) +from azure.cli.command_modules.appservice.utils import _rename_server_farm_props # pylint: disable=line-too-long from azure.cli.core.profiles import ResourceType @@ -61,6 +63,15 @@ class TestWebappMocked(unittest.TestCase): def setUp(self): self.client = WebSiteManagementClient(mock.MagicMock(), '123455678') + def test_rename_server_farm_props_uses_app_service_plan_id_for_new_sdk_models(self): + site = Site(location='westus') + site['serverFarmId'] = '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' + + _rename_server_farm_props(site) + + self.assertEqual(site['appServicePlanId'], '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') + self.assertNotIn('serverFarmId', site.keys()) + @mock.patch('azure.cli.command_modules.appservice.custom._update_site_source_control_properties_for_gh_action') @mock.patch('azure.cli.command_modules.appservice.custom._add_publish_profile_to_github') @mock.patch('azure.cli.command_modules.appservice.custom.prompt_y_n') diff --git a/src/azure-cli/azure/cli/command_modules/appservice/utils.py b/src/azure-cli/azure/cli/command_modules/appservice/utils.py index 1d6e10431bc..8840255b26c 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/utils.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/utils.py @@ -249,7 +249,14 @@ def _list_app(cli_ctx, resource_group_name=None): def _rename_server_farm_props(webapp): # Should be renamed in SDK in a future release server_farm_id = get_site_server_farm_id(webapp) - setattr(webapp, 'app_service_plan_id', server_farm_id) + try: + webapp["appServicePlanId"] = server_farm_id + except TypeError: + setattr(webapp, 'app_service_plan_id', server_farm_id) + try: + del webapp["serverFarmId"] + except (KeyError, TypeError): + pass # Remove server_farm_id if it exists as an attribute (for old SDK compatibility) if hasattr(webapp, 'server_farm_id'): try: From 56176e45b9a0ffae26f3efed68666712721f28f9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Jul 2026 03:31:57 +0000 Subject: [PATCH 03/11] [AppService] Cover webapp plan id rename regression --- .../tests/latest/test_webapp_commands_thru_mock.py | 10 ++++++++++ .../azure/cli/command_modules/appservice/utils.py | 10 ++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py index 38c0c5c21e0..00405e87db1 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py @@ -72,6 +72,16 @@ def test_rename_server_farm_props_uses_app_service_plan_id_for_new_sdk_models(se self.assertEqual(site['appServicePlanId'], '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') self.assertNotIn('serverFarmId', site.keys()) + def test_rename_server_farm_props_preserves_object_style_compatibility(self): + site = types.SimpleNamespace( + server_farm_id='/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') + + _rename_server_farm_props(site) + + self.assertEqual(site.app_service_plan_id, + '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') + self.assertFalse(hasattr(site, 'server_farm_id')) + @mock.patch('azure.cli.command_modules.appservice.custom._update_site_source_control_properties_for_gh_action') @mock.patch('azure.cli.command_modules.appservice.custom._add_publish_profile_to_github') @mock.patch('azure.cli.command_modules.appservice.custom.prompt_y_n') diff --git a/src/azure-cli/azure/cli/command_modules/appservice/utils.py b/src/azure-cli/azure/cli/command_modules/appservice/utils.py index 8840255b26c..d4cd621be18 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/utils.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/utils.py @@ -8,6 +8,7 @@ import urllib import urllib3 import certifi +from collections.abc import MutableMapping from datetime import datetime from knack.log import get_logger @@ -249,14 +250,11 @@ def _list_app(cli_ctx, resource_group_name=None): def _rename_server_farm_props(webapp): # Should be renamed in SDK in a future release server_farm_id = get_site_server_farm_id(webapp) - try: + if isinstance(webapp, MutableMapping): webapp["appServicePlanId"] = server_farm_id - except TypeError: + webapp.pop("serverFarmId", None) + else: setattr(webapp, 'app_service_plan_id', server_farm_id) - try: - del webapp["serverFarmId"] - except (KeyError, TypeError): - pass # Remove server_farm_id if it exists as an attribute (for old SDK compatibility) if hasattr(webapp, 'server_farm_id'): try: From 923901b2cc2a460429d23176c66be504ca96e9d1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Jul 2026 03:32:26 +0000 Subject: [PATCH 04/11] [AppService] Polish regression test names --- .../appservice/tests/latest/test_webapp_commands_thru_mock.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py index 00405e87db1..bab4fd89f5d 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py @@ -63,7 +63,7 @@ class TestWebappMocked(unittest.TestCase): def setUp(self): self.client = WebSiteManagementClient(mock.MagicMock(), '123455678') - def test_rename_server_farm_props_uses_app_service_plan_id_for_new_sdk_models(self): + def test_rename_server_farm_props_handles_mutable_mapping(self): site = Site(location='westus') site['serverFarmId'] = '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' @@ -72,7 +72,7 @@ def test_rename_server_farm_props_uses_app_service_plan_id_for_new_sdk_models(se self.assertEqual(site['appServicePlanId'], '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') self.assertNotIn('serverFarmId', site.keys()) - def test_rename_server_farm_props_preserves_object_style_compatibility(self): + def test_rename_server_farm_props_handles_object_attributes(self): site = types.SimpleNamespace( server_farm_id='/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') From 3901463eea81ce2d0b763c9df078faae8d465685 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Jul 2026 01:15:55 +0000 Subject: [PATCH 05/11] test: decouple appservice plan id regression from SDK Site mapping --- .../tests/latest/test_webapp_commands_thru_mock.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py index bab4fd89f5d..0bbf1062643 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py @@ -11,7 +11,6 @@ from azure.core.exceptions import HttpResponseError from azure.mgmt.web import WebSiteManagementClient -from azure.mgmt.web.models import Site from knack.util import CLIError from azure.cli.core.azclierror import (InvalidArgumentValueError, MutuallyExclusiveArgumentError, @@ -64,8 +63,10 @@ def setUp(self): self.client = WebSiteManagementClient(mock.MagicMock(), '123455678') def test_rename_server_farm_props_handles_mutable_mapping(self): - site = Site(location='westus') - site['serverFarmId'] = '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' + site = { + 'location': 'westus', + 'serverFarmId': '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' + } _rename_server_farm_props(site) From dd57dd2ca97b9d5cc830a57beb5a33b53634aefa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 13 Aug 2026 04:57:37 +0000 Subject: [PATCH 06/11] fix: rename flattened webapp plan property Co-authored-by: JaysonTaiMicrosoft <268525319+JaysonTaiMicrosoft@users.noreply.github.com> --- .../latest/test_webapp_commands_thru_mock.py | 19 +++++++++++++- .../cli/command_modules/appservice/utils.py | 25 +++++++++++++------ 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py index 0bbf1062643..26b6a35700d 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py @@ -40,7 +40,7 @@ list_startup_logs, show_startup_log, create_webapp) -from azure.cli.command_modules.appservice.utils import _rename_server_farm_props +from azure.cli.command_modules.appservice.utils import _rename_server_farm_props, get_site_server_farm_id # pylint: disable=line-too-long from azure.cli.core.profiles import ResourceType @@ -72,6 +72,23 @@ def test_rename_server_farm_props_handles_mutable_mapping(self): self.assertEqual(site['appServicePlanId'], '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') self.assertNotIn('serverFarmId', site.keys()) + self.assertEqual(get_site_server_farm_id(site), + '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') + + def test_rename_server_farm_props_handles_flattened_mutable_mapping(self): + site = { + 'properties': { + 'serverFarmId': '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' + } + } + + _rename_server_farm_props(site) + + self.assertEqual(site['properties']['appServicePlanId'], + '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') + self.assertNotIn('serverFarmId', site['properties']) + self.assertEqual(get_site_server_farm_id(site), + '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') def test_rename_server_farm_props_handles_object_attributes(self): site = types.SimpleNamespace( diff --git a/src/azure-cli/azure/cli/command_modules/appservice/utils.py b/src/azure-cli/azure/cli/command_modules/appservice/utils.py index d4cd621be18..9c31a15e13b 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/utils.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/utils.py @@ -54,13 +54,16 @@ def get_site_server_farm_id(site): In azure-mgmt-web 11.0.0+, models use hybrid dict/model nature with camelCase keys. This helper provides backward compatibility. """ - # Try new SDK dictionary-style access with camelCase - try: - return site["serverFarmId"] - except (KeyError, TypeError): - pass + # Try new SDK dictionary-style access with camelCase. The generated Site + # model stores flattened properties under "properties". + for source in (site, getattr(site, 'properties', None)): + if not isinstance(source, MutableMapping): + continue + for property_name in ('serverFarmId', 'appServicePlanId'): + if property_name in source: + return source[property_name] # Fall back to old SDK attribute access - return getattr(site, 'server_farm_id', None) + return getattr(site, 'server_farm_id', getattr(site, 'app_service_plan_id', None)) def str2bool(v): @@ -251,8 +254,14 @@ def _rename_server_farm_props(webapp): # Should be renamed in SDK in a future release server_farm_id = get_site_server_farm_id(webapp) if isinstance(webapp, MutableMapping): - webapp["appServicePlanId"] = server_farm_id - webapp.pop("serverFarmId", None) + properties = webapp.get("properties") + target = properties if isinstance(properties, MutableMapping) else webapp + target["appServicePlanId"] = server_farm_id + target.pop("serverFarmId", None) + try: + setattr(webapp, 'app_service_plan_id', server_farm_id) + except (AttributeError, TypeError): + pass else: setattr(webapp, 'app_service_plan_id', server_farm_id) # Remove server_farm_id if it exists as an attribute (for old SDK compatibility) From 6bf90bb8b81e7a02632d9e0f30c38e8c96173ca5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 13 Aug 2026 05:26:56 +0000 Subject: [PATCH 07/11] fix: ensure serverFarmId is removed from root-level mapping in _rename_server_farm_props Co-authored-by: a0x1ab <59631311+a0x1ab@users.noreply.github.com> --- .../latest/test_webapp_commands_thru_mock.py | 19 +++++++++++++++++++ .../cli/command_modules/appservice/utils.py | 19 +++++++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py index 26b6a35700d..bbfd3710fa1 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py @@ -87,9 +87,28 @@ def test_rename_server_farm_props_handles_flattened_mutable_mapping(self): self.assertEqual(site['properties']['appServicePlanId'], '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') self.assertNotIn('serverFarmId', site['properties']) + self.assertEqual(site['appServicePlanId'], + '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') self.assertEqual(get_site_server_farm_id(site), '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') + def test_rename_server_farm_props_removes_root_server_farm_id(self): + # Reproduces the az webapp list regression: newer SDK serializes serverFarmId directly + # at the root level (no "properties" wrapper), so it must be replaced there. + farm_id = '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' + site = { + 'location': 'australiaeast', + 'name': 'mywebapp', + 'serverFarmId': farm_id, + 'kind': 'app', + } + + _rename_server_farm_props(site) + + self.assertNotIn('serverFarmId', site) + self.assertEqual(site['appServicePlanId'], farm_id) + self.assertEqual(get_site_server_farm_id(site), farm_id) + def test_rename_server_farm_props_handles_object_attributes(self): site = types.SimpleNamespace( server_farm_id='/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') diff --git a/src/azure-cli/azure/cli/command_modules/appservice/utils.py b/src/azure-cli/azure/cli/command_modules/appservice/utils.py index 9c31a15e13b..811e024ed30 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/utils.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/utils.py @@ -56,7 +56,12 @@ def get_site_server_farm_id(site): """ # Try new SDK dictionary-style access with camelCase. The generated Site # model stores flattened properties under "properties". - for source in (site, getattr(site, 'properties', None)): + # For hybrid SDK objects, site.properties is an attribute; for plain dicts, fall back + # to the "properties" key so that unit-test fixtures (plain dicts) also work. + properties_source = getattr(site, 'properties', None) + if properties_source is None and isinstance(site, MutableMapping): + properties_source = site.get('properties') + for source in (site, properties_source): if not isinstance(source, MutableMapping): continue for property_name in ('serverFarmId', 'appServicePlanId'): @@ -255,9 +260,15 @@ def _rename_server_farm_props(webapp): server_farm_id = get_site_server_farm_id(webapp) if isinstance(webapp, MutableMapping): properties = webapp.get("properties") - target = properties if isinstance(properties, MutableMapping) else webapp - target["appServicePlanId"] = server_farm_id - target.pop("serverFarmId", None) + if isinstance(properties, MutableMapping): + # Newer SDK: properties are nested under "properties"; update there and also + # remove any root-level serverFarmId that may be present in the serialized form. + properties["appServicePlanId"] = server_farm_id + properties.pop("serverFarmId", None) + # Always remove serverFarmId from root and set appServicePlanId there too, + # because some SDK versions expose it flat (no "properties" wrapper). + webapp.pop("serverFarmId", None) + webapp["appServicePlanId"] = server_farm_id try: setattr(webapp, 'app_service_plan_id', server_farm_id) except (AttributeError, TypeError): From 58736820fd2c66275cbcc2e37fb27a19e12382ca Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 13 Aug 2026 06:51:31 +0000 Subject: [PATCH 08/11] fix: use post-serialisation transform to rename serverFarmId to appServicePlanId Co-authored-by: JaysonTaiMicrosoft <268525319+JaysonTaiMicrosoft@users.noreply.github.com> --- .../command_modules/appservice/commands.py | 34 +++++++++-- .../latest/test_webapp_commands_thru_mock.py | 60 ++++++++----------- .../cli/command_modules/appservice/utils.py | 37 ++++-------- 3 files changed, 66 insertions(+), 65 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/commands.py b/src/azure-cli/azure/cli/command_modules/appservice/commands.py index e734d104ff1..3bc089fd6de 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/commands.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/commands.py @@ -40,6 +40,22 @@ def transform_web_list_output(webs): return [transform_web_output(w) for w in webs] +def transform_rename_server_farm_id(web): + """Post-serialization transformer: rename serverFarmId to appServicePlanId. + + Newer azure-mgmt-web SDK models serialise their REST fields directly (bypassing + in-place dict writes), so we must rename the key here on the already-serialised + Python dict rather than on the model object in place. + """ + if isinstance(web, dict) and 'serverFarmId' in web and 'appServicePlanId' not in web: + web['appServicePlanId'] = web.pop('serverFarmId') + return web + + +def transform_rename_server_farm_id_list(webs): + return [transform_rename_server_farm_id(w) for w in webs] + + def transform_runtime_list_output(result): from collections import OrderedDict return [OrderedDict([ @@ -220,8 +236,10 @@ def load_command_table(self, _): deprecate_info=g.deprecate(redirect='webapp create and webapp deploy')) g.custom_command('ssh', 'ssh_webapp', exception_handler=ex_handler_factory(), is_preview=True) g.custom_command('exec', 'webapp_exec', custom_command_type=webapp_exec_custom, exception_handler=ex_handler_factory(), is_preview=True) - g.custom_command('list', 'list_webapp', table_transformer=transform_web_list_output) - g.custom_show_command('show', 'show_app', table_transformer=transform_web_output) + g.custom_command('list', 'list_webapp', transform=transform_rename_server_farm_id_list, + table_transformer=transform_web_list_output) + g.custom_show_command('show', 'show_app', transform=transform_rename_server_farm_id, + table_transformer=transform_web_output) g.custom_command('delete', 'delete_webapp') g.custom_command('stop', 'stop_webapp') g.custom_command('start', 'start_webapp') @@ -462,8 +480,10 @@ def load_command_table(self, _): validator=validate_functionapp) g.custom_command('list-runtimes', 'list_function_app_runtimes') g.custom_command('list-flexconsumption-runtimes', 'list_flex_function_app_runtimes') - g.custom_command('list', 'list_function_app', table_transformer=transform_web_list_output) - g.custom_show_command('show', 'show_functionapp', table_transformer=transform_web_output) + g.custom_command('list', 'list_function_app', transform=transform_rename_server_farm_id_list, + table_transformer=transform_web_list_output) + g.custom_show_command('show', 'show_functionapp', transform=transform_rename_server_farm_id, + table_transformer=transform_web_output) g.custom_command('delete', 'delete_function_app') g.custom_command('stop', 'stop_webapp') g.custom_command('start', 'start_webapp') @@ -686,8 +706,10 @@ def load_command_table(self, _): with self.command_group('logicapp', custom_command_type=logicapp_custom) as g: g.custom_command('create', 'create_logicapp', exception_handler=ex_handler_factory()) - g.custom_command('list', 'list_logicapp', table_transformer=transform_web_list_output) - g.custom_show_command('show', 'show_logicapp', table_transformer=transform_web_output) + g.custom_command('list', 'list_logicapp', transform=transform_rename_server_farm_id_list, + table_transformer=transform_web_list_output) + g.custom_show_command('show', 'show_logicapp', transform=transform_rename_server_farm_id, + table_transformer=transform_web_output) g.custom_command('scale', 'scale_logicapp', exception_handler=ex_handler_factory()) with self.command_group('logicapp config appsettings', custom_command_type=logicapp_custom) as g: diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py index a3c7abed9e8..67a0910a186 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py @@ -43,6 +43,8 @@ troubleshoot_status, create_webapp) from azure.cli.command_modules.appservice.utils import _rename_server_farm_props, get_site_server_farm_id +from azure.cli.command_modules.appservice.commands import (transform_rename_server_farm_id, + transform_rename_server_farm_id_list) # pylint: disable=line-too-long from azure.cli.core.profiles import ResourceType @@ -64,52 +66,40 @@ class TestWebappMocked(unittest.TestCase): def setUp(self): self.client = WebSiteManagementClient(mock.MagicMock(), '123455678') - def test_rename_server_farm_props_handles_mutable_mapping(self): - site = { + def test_transform_rename_server_farm_id_renames_key(self): + # Verifies the post-serialisation transformer renames serverFarmId -> appServicePlanId + farm_id = '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' + web = { 'location': 'westus', - 'serverFarmId': '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' + 'serverFarmId': farm_id, } - _rename_server_farm_props(site) + result = transform_rename_server_farm_id(web) - self.assertEqual(site['appServicePlanId'], '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') - self.assertNotIn('serverFarmId', site.keys()) - self.assertEqual(get_site_server_farm_id(site), - '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') + self.assertEqual(result['appServicePlanId'], farm_id) + self.assertNotIn('serverFarmId', result) - def test_rename_server_farm_props_handles_flattened_mutable_mapping(self): - site = { - 'properties': { - 'serverFarmId': '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' - } - } + def test_transform_rename_server_farm_id_noop_when_already_renamed(self): + farm_id = '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' + web = {'appServicePlanId': farm_id} - _rename_server_farm_props(site) + result = transform_rename_server_farm_id(web) - self.assertEqual(site['properties']['appServicePlanId'], - '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') - self.assertNotIn('serverFarmId', site['properties']) - self.assertEqual(site['appServicePlanId'], - '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') - self.assertEqual(get_site_server_farm_id(site), - '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan') + self.assertEqual(result['appServicePlanId'], farm_id) + self.assertNotIn('serverFarmId', result) - def test_rename_server_farm_props_removes_root_server_farm_id(self): - # Reproduces the az webapp list regression: newer SDK serializes serverFarmId directly - # at the root level (no "properties" wrapper), so it must be replaced there. + def test_transform_rename_server_farm_id_list(self): farm_id = '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' - site = { - 'location': 'australiaeast', - 'name': 'mywebapp', - 'serverFarmId': farm_id, - 'kind': 'app', - } + webs = [ + {'serverFarmId': farm_id, 'name': 'app1'}, + {'appServicePlanId': farm_id, 'name': 'app2'}, + ] - _rename_server_farm_props(site) + results = transform_rename_server_farm_id_list(webs) - self.assertNotIn('serverFarmId', site) - self.assertEqual(site['appServicePlanId'], farm_id) - self.assertEqual(get_site_server_farm_id(site), farm_id) + for r in results: + self.assertNotIn('serverFarmId', r) + self.assertEqual(r['appServicePlanId'], farm_id) def test_rename_server_farm_props_handles_object_attributes(self): site = types.SimpleNamespace( diff --git a/src/azure-cli/azure/cli/command_modules/appservice/utils.py b/src/azure-cli/azure/cli/command_modules/appservice/utils.py index 811e024ed30..1ed29df396c 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/utils.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/utils.py @@ -256,31 +256,20 @@ def _list_app(cli_ctx, resource_group_name=None): def _rename_server_farm_props(webapp): - # Should be renamed in SDK in a future release - server_farm_id = get_site_server_farm_id(webapp) - if isinstance(webapp, MutableMapping): - properties = webapp.get("properties") - if isinstance(properties, MutableMapping): - # Newer SDK: properties are nested under "properties"; update there and also - # remove any root-level serverFarmId that may be present in the serialized form. - properties["appServicePlanId"] = server_farm_id - properties.pop("serverFarmId", None) - # Always remove serverFarmId from root and set appServicePlanId there too, - # because some SDK versions expose it flat (no "properties" wrapper). - webapp.pop("serverFarmId", None) - webapp["appServicePlanId"] = server_farm_id - try: - setattr(webapp, 'app_service_plan_id', server_farm_id) - except (AttributeError, TypeError): - pass - else: + # Newer SDK models (MutableMapping-based) are handled by the command-level + # transform (transform_rename_server_farm_id / _list) which operates on the + # already-serialised dict. Here we only need to cover the legacy attribute-style + # objects so that downstream code (e.g. get_site_server_farm_id) can still read + # app_service_plan_id from older SDK models. + if not isinstance(webapp, MutableMapping): + server_farm_id = get_site_server_farm_id(webapp) setattr(webapp, 'app_service_plan_id', server_farm_id) - # Remove server_farm_id if it exists as an attribute (for old SDK compatibility) - if hasattr(webapp, 'server_farm_id'): - try: - del webapp.server_farm_id - except (AttributeError, TypeError): - pass + # Remove server_farm_id attribute for old SDK compatibility + if hasattr(webapp, 'server_farm_id'): + try: + del webapp.server_farm_id + except (AttributeError, TypeError): + pass return webapp From a5c9b9182f7031c0c4751a832ffeffc23fcb3b2e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Aug 2026 04:34:22 +0000 Subject: [PATCH 09/11] fix: serialize model in transformer before renaming serverFarmId to appServicePlanId Co-authored-by: a0x1ab <59631311+a0x1ab@users.noreply.github.com> --- .../command_modules/appservice/commands.py | 33 ++++++++++++++++--- .../latest/test_webapp_commands_thru_mock.py | 33 +++++++++++++++++++ 2 files changed, 61 insertions(+), 5 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/commands.py b/src/azure-cli/azure/cli/command_modules/appservice/commands.py index 3bc089fd6de..e3a087c0885 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/commands.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/commands.py @@ -43,12 +43,35 @@ def transform_web_list_output(webs): def transform_rename_server_farm_id(web): """Post-serialization transformer: rename serverFarmId to appServicePlanId. - Newer azure-mgmt-web SDK models serialise their REST fields directly (bypassing - in-place dict writes), so we must rename the key here on the already-serialised - Python dict rather than on the model object in place. + The command-level 'transform' hook runs *before* the pipeline calls todict(), + so the value received here is a raw model object, not yet a plain dict. We + therefore serialise it ourselves first using the same todict() the pipeline + would use, rename the key in the resulting dict, and return the dict so that + the pipeline's subsequent todict() call passes through it unchanged. + + In newer azure-mgmt-web SDK versions (ARM-envelope layout) serverFarmId is + nested under 'properties'. In older flat-layout versions it appears at the + top level. Both cases are handled. """ - if isinstance(web, dict) and 'serverFarmId' in web and 'appServicePlanId' not in web: - web['appServicePlanId'] = web.pop('serverFarmId') + from azure.cli.core.util import todict + from azure.cli.core.commands import AzCliCommandInvoker + + if not isinstance(web, dict): + web = todict(web, AzCliCommandInvoker.remove_additional_prop_layer) + + if not isinstance(web, dict): + return web + + # serverFarmId may be at the top level (older/flat SDK serialisation) or + # nested under 'properties' (newer ARM-envelope SDK serialisation). + server_farm_id = web.pop('serverFarmId', None) + properties = web.get('properties') + if isinstance(properties, dict): + server_farm_id = properties.pop('serverFarmId', server_farm_id) + + if server_farm_id is not None and 'appServicePlanId' not in web: + web['appServicePlanId'] = server_farm_id + return web diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py index 67a0910a186..3605bb589c9 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py @@ -79,6 +79,39 @@ def test_transform_rename_server_farm_id_renames_key(self): self.assertEqual(result['appServicePlanId'], farm_id) self.assertNotIn('serverFarmId', result) + def test_transform_rename_server_farm_id_nested_under_properties(self): + # New SDK ARM-envelope layout: serverFarmId is under 'properties', not at the top level + farm_id = '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' + web = { + 'location': 'westus', + 'properties': { + 'serverFarmId': farm_id, + 'name': 'myapp', + }, + } + + result = transform_rename_server_farm_id(web) + + self.assertEqual(result['appServicePlanId'], farm_id) + self.assertNotIn('serverFarmId', result) + self.assertNotIn('serverFarmId', result.get('properties', {})) + + def test_transform_rename_server_farm_id_model_object(self): + # When the transformer receives a raw model object (before todict), it must + # serialise the object first and then rename serverFarmId. + farm_id = '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' + # Simulate a model object with __dict__ that todict() will expand. + # Note: attribute names with underscores are converted to camelCase by todict(), + # so set them as camelCase to match what todict() would produce. + web_obj = types.SimpleNamespace(location='westus') + web_obj.__dict__['serverFarmId'] = farm_id + + with mock.patch('azure.cli.core.util.todict', return_value={'location': 'westus', 'serverFarmId': farm_id}): + result = transform_rename_server_farm_id(web_obj) + + self.assertEqual(result['appServicePlanId'], farm_id) + self.assertNotIn('serverFarmId', result) + def test_transform_rename_server_farm_id_noop_when_already_renamed(self): farm_id = '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' web = {'appServicePlanId': farm_id} From ec3a6fbe3db1fa5daf33cb0defa8ce65a1624de0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Aug 2026 14:13:26 +0000 Subject: [PATCH 10/11] fix: add appServicePlanId alongside serverFarmId to preserve backward compatibility The transformer was removing serverFarmId from the output which broke existing live tests in test_logicapp_commands.py and test_functionapp_commands.py that explicitly check for serverFarmId in the output. Fix by using get() instead of pop() so that serverFarmId is preserved while appServicePlanId is added as an alias. Co-authored-by: a0x1ab <59631311+a0x1ab@users.noreply.github.com> --- .../command_modules/appservice/commands.py | 20 ++++++++++++------- .../latest/test_webapp_commands_thru_mock.py | 15 +++++++------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/commands.py b/src/azure-cli/azure/cli/command_modules/appservice/commands.py index e3a087c0885..8f567a740ba 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/commands.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/commands.py @@ -41,13 +41,17 @@ def transform_web_list_output(webs): def transform_rename_server_farm_id(web): - """Post-serialization transformer: rename serverFarmId to appServicePlanId. + """Post-serialization transformer: expose appServicePlanId in output. The command-level 'transform' hook runs *before* the pipeline calls todict(), so the value received here is a raw model object, not yet a plain dict. We therefore serialise it ourselves first using the same todict() the pipeline - would use, rename the key in the resulting dict, and return the dict so that - the pipeline's subsequent todict() call passes through it unchanged. + would use, add appServicePlanId to the resulting dict, and return the dict so + that the pipeline's subsequent todict() call passes through it unchanged. + + serverFarmId is preserved in the output for backward compatibility with + existing scripts and tests. appServicePlanId is added alongside it as an + alias so that callers relying on the original field name continue to work. In newer azure-mgmt-web SDK versions (ARM-envelope layout) serverFarmId is nested under 'properties'. In older flat-layout versions it appears at the @@ -64,10 +68,12 @@ def transform_rename_server_farm_id(web): # serverFarmId may be at the top level (older/flat SDK serialisation) or # nested under 'properties' (newer ARM-envelope SDK serialisation). - server_farm_id = web.pop('serverFarmId', None) - properties = web.get('properties') - if isinstance(properties, dict): - server_farm_id = properties.pop('serverFarmId', server_farm_id) + # Use get (not pop) so that serverFarmId is preserved for backward compat. + server_farm_id = web.get('serverFarmId') + if server_farm_id is None: + properties = web.get('properties') + if isinstance(properties, dict): + server_farm_id = properties.get('serverFarmId') if server_farm_id is not None and 'appServicePlanId' not in web: web['appServicePlanId'] = server_farm_id diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py index 3605bb589c9..1bfcc5612f2 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py @@ -67,7 +67,7 @@ def setUp(self): self.client = WebSiteManagementClient(mock.MagicMock(), '123455678') def test_transform_rename_server_farm_id_renames_key(self): - # Verifies the post-serialisation transformer renames serverFarmId -> appServicePlanId + # Verifies the post-serialisation transformer adds appServicePlanId alongside serverFarmId farm_id = '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' web = { 'location': 'westus', @@ -77,7 +77,8 @@ def test_transform_rename_server_farm_id_renames_key(self): result = transform_rename_server_farm_id(web) self.assertEqual(result['appServicePlanId'], farm_id) - self.assertNotIn('serverFarmId', result) + # serverFarmId is preserved for backward compatibility + self.assertEqual(result['serverFarmId'], farm_id) def test_transform_rename_server_farm_id_nested_under_properties(self): # New SDK ARM-envelope layout: serverFarmId is under 'properties', not at the top level @@ -93,12 +94,12 @@ def test_transform_rename_server_farm_id_nested_under_properties(self): result = transform_rename_server_farm_id(web) self.assertEqual(result['appServicePlanId'], farm_id) - self.assertNotIn('serverFarmId', result) - self.assertNotIn('serverFarmId', result.get('properties', {})) + # serverFarmId is preserved in properties for backward compatibility + self.assertEqual(result['properties']['serverFarmId'], farm_id) def test_transform_rename_server_farm_id_model_object(self): # When the transformer receives a raw model object (before todict), it must - # serialise the object first and then rename serverFarmId. + # serialise the object first and then add appServicePlanId. farm_id = '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' # Simulate a model object with __dict__ that todict() will expand. # Note: attribute names with underscores are converted to camelCase by todict(), @@ -110,7 +111,8 @@ def test_transform_rename_server_farm_id_model_object(self): result = transform_rename_server_farm_id(web_obj) self.assertEqual(result['appServicePlanId'], farm_id) - self.assertNotIn('serverFarmId', result) + # serverFarmId is preserved for backward compatibility + self.assertEqual(result['serverFarmId'], farm_id) def test_transform_rename_server_farm_id_noop_when_already_renamed(self): farm_id = '/subscriptions/sub/resourceGroups/rg/providers/Microsoft.Web/serverfarms/plan' @@ -131,7 +133,6 @@ def test_transform_rename_server_farm_id_list(self): results = transform_rename_server_farm_id_list(webs) for r in results: - self.assertNotIn('serverFarmId', r) self.assertEqual(r['appServicePlanId'], farm_id) def test_rename_server_farm_props_handles_object_attributes(self): From 3a123844d3176dbf0f75e76ffd7a724e299de202 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Aug 2026 16:49:56 +0000 Subject: [PATCH 11/11] fix: resolve flake8 style errors in test_webapp_commands_thru_mock.py Co-authored-by: a0x1ab <59631311+a0x1ab@users.noreply.github.com> --- .../latest/test_webapp_commands_thru_mock.py | 69 +++++++++---------- 1 file changed, 33 insertions(+), 36 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py index 1bfcc5612f2..00902217530 100644 --- a/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py +++ b/src/azure-cli/azure/cli/command_modules/appservice/tests/latest/test_webapp_commands_thru_mock.py @@ -44,7 +44,7 @@ create_webapp) from azure.cli.command_modules.appservice.utils import _rename_server_farm_props, get_site_server_farm_id from azure.cli.command_modules.appservice.commands import (transform_rename_server_farm_id, - transform_rename_server_farm_id_list) + transform_rename_server_farm_id_list) # pylint: disable=line-too-long from azure.cli.core.profiles import ResourceType @@ -467,8 +467,6 @@ def test_restore_snapshot(self, generic_site_op_mock, client_factory_mock): generic_site_op_mock.return_value = site client_factory_mock.return_value = client - - SnapshotRecoverySource, SnapshotRestoreRequest = \ cmd_mock.get_models('SnapshotRecoverySource', 'SnapshotRestoreRequest') @@ -589,14 +587,13 @@ def test_create_managed_ssl_cert(self, generic_site_op_mock, client_factory_mock client.certificates.create_or_update.assert_called_once_with(name=host_name, resource_group_name=rg_name, certificate_envelope=cert_def) - def test_update_app_settings_error_handling_no_parameters(self): """Test that MutuallyExclusiveArgumentError is raised when neither settings nor slot_settings are provided.""" cmd_mock = _get_test_cmd() - + # Test missing both parameters - should fail early without calling any services - with self.assertRaisesRegex(MutuallyExclusiveArgumentError, - "Please provide either --settings or --slot-settings parameter"): + with self.assertRaisesRegex(MutuallyExclusiveArgumentError, + "Please provide either --settings or --slot-settings parameter"): update_app_settings(cmd_mock, 'test-rg', 'test-app') @mock.patch('azure.cli.command_modules.appservice.custom._generic_site_operation') @@ -604,19 +601,19 @@ def test_update_app_settings_error_handling_no_parameters(self): def test_update_app_settings_error_handling_invalid_format(self, mock_json_parse, mock_site_op): """Test that InvalidArgumentValueError is raised for invalid setting formats.""" cmd_mock = _get_test_cmd() - + # Setup minimal mocks needed to reach the error handling code mock_app_settings = mock.MagicMock() mock_app_settings.properties = {} mock_site_op.return_value = mock_app_settings - + # Mock shell_safe_json_parse to raise InvalidArgumentValueError (simulating invalid JSON) mock_json_parse.side_effect = InvalidArgumentValueError("Invalid JSON format") - + # Test invalid format that can't be parsed as JSON or key=value invalid_setting = "invalid_format_no_equals_no_json" expected_message = r"Invalid setting format.*Expected 'key=value' format or valid JSON" - + with self.assertRaisesRegex(InvalidArgumentValueError, expected_message): update_app_settings(cmd_mock, 'test-rg', 'test-app', settings=[invalid_setting]) @@ -625,19 +622,19 @@ def test_update_app_settings_error_handling_invalid_format(self, mock_json_parse def test_update_app_settings_error_handling_invalid_format_no_equals(self, mock_json_parse, mock_site_op): """Test ValueError path when shell_safe_json_parse raises InvalidArgumentValueError and string contains no '='.""" cmd_mock = _get_test_cmd() - + # Setup minimal mocks needed to reach the error handling code mock_app_settings = mock.MagicMock() mock_app_settings.properties = {} mock_site_op.return_value = mock_app_settings - + # Mock shell_safe_json_parse to raise InvalidArgumentValueError mock_json_parse.side_effect = InvalidArgumentValueError("Invalid JSON format") - + # Test invalid format with no equals sign - this should trigger ValueError in split('=', 1) invalid_setting_no_equals = "invalidformatthatcontainsnoequalsign" expected_message = r"Invalid setting format.*Expected 'key=value' format or valid JSON" - + with self.assertRaisesRegex(InvalidArgumentValueError, expected_message): update_app_settings(cmd_mock, 'test-rg', 'test-app', settings=[invalid_setting_no_equals]) @@ -646,26 +643,26 @@ def test_update_app_settings_error_handling_invalid_format_no_equals(self, mock_ @mock.patch('azure.cli.command_modules.appservice.custom.is_centauri_functionapp') @mock.patch('azure.cli.command_modules.appservice.custom._generic_settings_operation') @mock.patch('azure.cli.command_modules.appservice.custom._build_app_settings_output') - def test_update_app_settings_success_key_value_format(self, mock_build, mock_settings_op, mock_centauri, - mock_client_factory, mock_site_op): + def test_update_app_settings_success_key_value_format(self, mock_build, mock_settings_op, mock_centauri, + mock_client_factory, mock_site_op): """Test successful processing of key=value format settings.""" cmd_mock = _get_test_cmd() - + # Setup mocks mock_app_settings = mock.MagicMock() mock_app_settings.properties = {} mock_site_op.return_value = mock_app_settings - + mock_client = mock.MagicMock() mock_client_factory.return_value = mock_client mock_centauri.return_value = False mock_settings_op.return_value = mock_app_settings mock_build.return_value = {"KEY1": "value1", "KEY2": "value2"} - + # Test valid key=value format - result = update_app_settings(cmd_mock, 'test-rg', 'test-app', - settings=['KEY1=value1', 'KEY2=value2']) - + result = update_app_settings(cmd_mock, 'test-rg', 'test-app', + settings=['KEY1=value1', 'KEY2=value2']) + # Verify the function completed successfully self.assertEqual(result["KEY1"], "value1") self.assertEqual(result["KEY2"], "value2") @@ -675,20 +672,20 @@ def test_update_app_settings_success_key_value_format(self, mock_build, mock_set def test_update_application_settings_polling_error_handling(self, mock_send_request): """Test that AzureResponseError is raised in polling function when appropriate.""" cmd_mock = _get_test_cmd() - + # Mock an exception that doesn't have the expected structure class MockException(Exception): def __init__(self): self.response = mock.MagicMock() self.response.status_code = 400 # Not 202 self.response.headers = {} - + # Mock _generic_settings_operation to raise the exception with mock.patch('azure.cli.command_modules.appservice.custom._generic_settings_operation') as mock_settings_op, \ self.assertRaisesRegex(AzureResponseError, "Failed to update application settings"): mock_settings_op.side_effect = MockException() - update_application_settings_polling(cmd_mock, 'test-rg', 'test-app', - mock.MagicMock(), None, mock.MagicMock()) + update_application_settings_polling(cmd_mock, 'test-rg', 'test-app', + mock.MagicMock(), None, mock.MagicMock()) @mock.patch('azure.cli.command_modules.appservice.custom._generic_site_operation') @mock.patch('azure.cli.command_modules.appservice.custom.web_client_factory') @@ -696,15 +693,15 @@ def __init__(self): @mock.patch('azure.cli.command_modules.appservice.custom._generic_settings_operation') @mock.patch('azure.cli.command_modules.appservice.custom._build_app_settings_output') def test_update_app_settings_success_with_slot_settings(self, mock_build, mock_settings_op, mock_centauri, - mock_client_factory, mock_site_op): + mock_client_factory, mock_site_op): """Test successful processing with slot settings.""" cmd_mock = _get_test_cmd() - + # Setup mocks mock_app_settings = mock.MagicMock() mock_app_settings.properties = {} mock_site_op.return_value = mock_app_settings - + mock_client = mock.MagicMock() mock_slot_config = mock.MagicMock() mock_slot_config.app_setting_names = [] @@ -713,12 +710,12 @@ def test_update_app_settings_success_with_slot_settings(self, mock_build, mock_s mock_centauri.return_value = False mock_settings_op.return_value = mock_app_settings mock_build.return_value = {"SLOT_KEY": "slot_value"} - + # Test with slot settings - result = update_app_settings(cmd_mock, 'test-rg', 'test-app', - settings=['REGULAR_KEY=regular_value'], - slot_settings=['SLOT_KEY=slot_value']) - + update_app_settings(cmd_mock, 'test-rg', 'test-app', + settings=['REGULAR_KEY=regular_value'], + slot_settings=['SLOT_KEY=slot_value']) + # Verify slot configuration was updated mock_client.web_apps.list_slot_configuration_names.assert_called_once() mock_client.web_apps.update_slot_configuration_names.assert_called_once() @@ -2248,4 +2245,4 @@ def test_get_java_runtimes_from_container_settings_reads_mapping(self): if __name__ == '__main__': - unittest.main() \ No newline at end of file + unittest.main()