From 23bd8fcbee203948323a7b176d9a06e28a4fc7f9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 16:07:56 +0000 Subject: [PATCH 1/3] Initial plan From 43bc36c6f78db464e6523fd382ac65cfc4e6f262 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 16:19:22 +0000 Subject: [PATCH 2/3] [RDBMS] Fix Azure/azure-cli#33776: `az postgres flexible-server replica promote`: Populate sourceServerResourceId when doing a planned promote --- src/azure-cli/HISTORY.rst | 1 + .../postgresql/commands/replica_commands.py | 5 + .../tests/unit/test_replica_commands.py | 128 ++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 src/azure-cli/azure/cli/command_modules/postgresql/tests/unit/test_replica_commands.py diff --git a/src/azure-cli/HISTORY.rst b/src/azure-cli/HISTORY.rst index 7bbafa33310..bed2921f09a 100644 --- a/src/azure-cli/HISTORY.rst +++ b/src/azure-cli/HISTORY.rst @@ -96,6 +96,7 @@ Release History * `az postgres flexible-server create/restore/geo-restore/replica create`: Add new arguments `--federated-client-id` and `--backup-federated-client-id` to support multi-tenant application registration (#33645) * `az postgresql flexible-server maintenance-event list/show/apply-now/reschedule`: Add commands for maintenance events (#33662) +* Fix #33776: `az postgres flexible-server replica promote`: Populate `sourceServerResourceId` in the PATCH body so that planned switchover promote no longer fails with `MissingRequiredParameter` **Resource** diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/commands/replica_commands.py b/src/azure-cli/azure/cli/command_modules/postgresql/commands/replica_commands.py index 36cbd6e8eb3..11926f3cc91 100644 --- a/src/azure-cli/azure/cli/command_modules/postgresql/commands/replica_commands.py +++ b/src/azure-cli/azure/cli/command_modules/postgresql/commands/replica_commands.py @@ -187,6 +187,11 @@ def flexible_replica_promote(cmd, client, resource_group_name, name, promote_mod ) ) + # The service requires sourceServerResourceId in the PATCH body when promoting a replica. + # Populate it from the replica server's existing source server resource ID. + if server_object.source_server_resource_id: + params['properties']['sourceServerResourceId'] = server_object.source_server_resource_id + return client.begin_update(resource_group_name, name, params) diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/tests/unit/test_replica_commands.py b/src/azure-cli/azure/cli/command_modules/postgresql/tests/unit/test_replica_commands.py new file mode 100644 index 00000000000..47355f87e3e --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/postgresql/tests/unit/test_replica_commands.py @@ -0,0 +1,128 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +import unittest +from unittest.mock import MagicMock, patch + + +class ReplicaCommandsTest(unittest.TestCase): + """Unit tests for PostgreSQL flexible-server replica commands.""" + + def setUp(self): + self.resource_group = 'test-rg' + self.server_name = 'test-replica' + self.source_server_resource_id = ( + '/subscriptions/sub-id/resourceGroups/test-rg' + '/providers/Microsoft.DBforPostgreSQL/flexibleServers/source-server' + ) + + def _build_server_object(self, role='AsyncReplica'): + server = MagicMock() + server.replica.role = role + server.source_server_resource_id = self.source_server_resource_id + return server + + def _make_cmd_mock(self): + cmd = MagicMock() + cmd.cli_ctx = MagicMock() + return cmd + + @patch('azure.cli.command_modules.postgresql.commands.replica_commands.is_citus_cluster', return_value=False) + @patch('azure.cli.command_modules.postgresql.commands.replica_commands.validate_resource_group') + def test_flexible_replica_promote_switchover_includes_source_server_resource_id( + self, mock_validate_rg, mock_is_citus): + """Regression test for #33776: planned switchover must populate sourceServerResourceId.""" + from azure.cli.command_modules.postgresql.commands.replica_commands import flexible_replica_promote + + mock_client = MagicMock() + server_object = self._build_server_object(role='AsyncReplica') + mock_client.get.return_value = server_object + + flexible_replica_promote( + cmd=self._make_cmd_mock(), + client=mock_client, + resource_group_name=self.resource_group, + name=self.server_name, + promote_mode='switchover', + promote_option='planned', + ) + + mock_client.begin_update.assert_called_once() + _, call_kwargs = mock_client.begin_update.call_args + # begin_update is called as positional: (resource_group, name, params) + call_args = mock_client.begin_update.call_args[0] + params = call_args[2] + + # Verify sourceServerResourceId is included in the PATCH body + self.assertEqual( + params['properties']['sourceServerResourceId'], + self.source_server_resource_id, + ) + # Verify replica role and promote settings are correct + self.assertEqual(params['properties']['replica']['role'], 'Primary') + self.assertEqual(params['properties']['replica']['promoteMode'], 'switchover') + self.assertEqual(params['properties']['replica']['promoteOption'], 'planned') + + @patch('azure.cli.command_modules.postgresql.commands.replica_commands.is_citus_cluster', return_value=False) + @patch('azure.cli.command_modules.postgresql.commands.replica_commands.validate_resource_group') + def test_flexible_replica_promote_standalone_includes_source_server_resource_id( + self, mock_validate_rg, mock_is_citus): + """Standalone promote also populates sourceServerResourceId in the PATCH body.""" + from azure.cli.command_modules.postgresql.commands.replica_commands import flexible_replica_promote + + mock_client = MagicMock() + server_object = self._build_server_object(role='AsyncReplica') + mock_client.get.return_value = server_object + + flexible_replica_promote( + cmd=self._make_cmd_mock(), + client=mock_client, + resource_group_name=self.resource_group, + name=self.server_name, + promote_mode='standalone', + promote_option='planned', + ) + + mock_client.begin_update.assert_called_once() + call_args = mock_client.begin_update.call_args[0] + params = call_args[2] + + self.assertEqual( + params['properties']['sourceServerResourceId'], + self.source_server_resource_id, + ) + self.assertEqual(params['properties']['replica']['role'], 'None') + + @patch('azure.cli.command_modules.postgresql.commands.replica_commands.is_citus_cluster', return_value=False) + @patch('azure.cli.command_modules.postgresql.commands.replica_commands.validate_resource_group') + def test_flexible_replica_promote_no_source_server_resource_id_does_not_fail( + self, mock_validate_rg, mock_is_citus): + """If source_server_resource_id is absent on the server object, no KeyError is raised.""" + from azure.cli.command_modules.postgresql.commands.replica_commands import flexible_replica_promote + + mock_client = MagicMock() + server_object = self._build_server_object(role='AsyncReplica') + server_object.source_server_resource_id = None + mock_client.get.return_value = server_object + + flexible_replica_promote( + cmd=self._make_cmd_mock(), + client=mock_client, + resource_group_name=self.resource_group, + name=self.server_name, + promote_mode='switchover', + promote_option='forced', + ) + + mock_client.begin_update.assert_called_once() + call_args = mock_client.begin_update.call_args[0] + params = call_args[2] + + # sourceServerResourceId should NOT be injected when the server has none + self.assertNotIn('sourceServerResourceId', params['properties']) + + +if __name__ == '__main__': + unittest.main() From a385c994628afc5b5fb49cdc4e8c9a68c1fae9d7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 23 Jul 2026 19:29:29 +0000 Subject: [PATCH 3/3] Fix CI failures: move test to tests/latest/, remove unused var, add PR# to HISTORY.rst --- src/azure-cli/HISTORY.rst | 2 +- .../test_postgres_flexible_commands_replica_promote.py} | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) rename src/azure-cli/azure/cli/command_modules/postgresql/tests/{unit/test_replica_commands.py => latest/test_postgres_flexible_commands_replica_promote.py} (98%) diff --git a/src/azure-cli/HISTORY.rst b/src/azure-cli/HISTORY.rst index bed2921f09a..87412985c9f 100644 --- a/src/azure-cli/HISTORY.rst +++ b/src/azure-cli/HISTORY.rst @@ -96,7 +96,7 @@ Release History * `az postgres flexible-server create/restore/geo-restore/replica create`: Add new arguments `--federated-client-id` and `--backup-federated-client-id` to support multi-tenant application registration (#33645) * `az postgresql flexible-server maintenance-event list/show/apply-now/reschedule`: Add commands for maintenance events (#33662) -* Fix #33776: `az postgres flexible-server replica promote`: Populate `sourceServerResourceId` in the PATCH body so that planned switchover promote no longer fails with `MissingRequiredParameter` +* Fix #33776: `az postgres flexible-server replica promote`: Populate `sourceServerResourceId` in the PATCH body so that planned switchover promote no longer fails with `MissingRequiredParameter` (#33777) **Resource** diff --git a/src/azure-cli/azure/cli/command_modules/postgresql/tests/unit/test_replica_commands.py b/src/azure-cli/azure/cli/command_modules/postgresql/tests/latest/test_postgres_flexible_commands_replica_promote.py similarity index 98% rename from src/azure-cli/azure/cli/command_modules/postgresql/tests/unit/test_replica_commands.py rename to src/azure-cli/azure/cli/command_modules/postgresql/tests/latest/test_postgres_flexible_commands_replica_promote.py index 47355f87e3e..8fcabe9e5c8 100644 --- a/src/azure-cli/azure/cli/command_modules/postgresql/tests/unit/test_replica_commands.py +++ b/src/azure-cli/azure/cli/command_modules/postgresql/tests/latest/test_postgres_flexible_commands_replica_promote.py @@ -50,7 +50,6 @@ def test_flexible_replica_promote_switchover_includes_source_server_resource_id( ) mock_client.begin_update.assert_called_once() - _, call_kwargs = mock_client.begin_update.call_args # begin_update is called as positional: (resource_group, name, params) call_args = mock_client.begin_update.call_args[0] params = call_args[2]