From 8c3d0c45758c89fe5e93a1fec5b1f14d2b2c50de Mon Sep 17 00:00:00 2001 From: Korede Okelowo Date: Fri, 9 Oct 2026 15:24:07 -0500 Subject: [PATCH 1/3] Fix Containerapp job execution pagination Follow ARM continuation URLs and return complete execution history. Add an optional page-size argument with regression coverage. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 445d57df-fd66-4c9d-b6a0-ee9c14ca213d --- .../command_modules/containerapp/_clients.py | 20 +- .../cli/command_modules/containerapp/_help.py | 3 + .../command_modules/containerapp/_params.py | 4 + .../command_modules/containerapp/custom.py | 8 +- ...st_containerappjob_execution_pagination.py | 218 ++++++++++++++++++ 5 files changed, 248 insertions(+), 5 deletions(-) create mode 100644 src/azure-cli/azure/cli/command_modules/containerapp/tests/latest/test_containerappjob_execution_pagination.py diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/_clients.py b/src/azure-cli/azure/cli/command_modules/containerapp/_clients.py index 319afc36b28..224dd5d988a 100644 --- a/src/azure-cli/azure/cli/command_modules/containerapp/_clients.py +++ b/src/azure-cli/azure/cli/command_modules/containerapp/_clients.py @@ -10,7 +10,7 @@ import sys from azure.cli.core.azclierror import AzureResponseError, ResourceNotFoundError -from azure.cli.core.util import send_raw_request +from azure.cli.core.util import is_same_origin, send_raw_request from azure.cli.core.commands.client_factory import get_subscription_id from knack.log import get_logger @@ -1043,7 +1043,7 @@ def stop_job(cls, cmd, resource_group_name, name, job_execution_name): return r @classmethod - def get_executions(cls, cmd, resource_group_name, name): + def get_executions(cls, cmd, resource_group_name, name, page_size=None): management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager sub_id = get_subscription_id(cmd.cli_ctx) url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/jobs/{}/executions?api-version={}" @@ -1054,8 +1054,22 @@ def get_executions(cls, cmd, resource_group_name, name): name, cls.api_version) + if page_size is not None: + request_url += f"&pageSize={page_size}" + r = send_raw_request(cmd.cli_ctx, "GET", request_url) - return r.json() + response = r.json() + page = response + while page.get("nextLink"): + request_url = page["nextLink"] + if not is_same_origin(request_url, management_hostname): + raise AzureResponseError( + "Invalid nextLink in job executions response: expected the active cloud's ARM endpoint.") + page = send_raw_request(cmd.cli_ctx, "GET", request_url).json() + response["value"].extend(page["value"]) + response["nextLink"] = page.get("nextLink") + + return response @classmethod def get_single_execution(cls, cmd, resource_group_name, name, job_execution_name): diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/_help.py b/src/azure-cli/azure/cli/command_modules/containerapp/_help.py index d9a9505a3c0..c42811d00e3 100644 --- a/src/azure-cli/azure/cli/command_modules/containerapp/_help.py +++ b/src/azure-cli/azure/cli/command_modules/containerapp/_help.py @@ -766,9 +766,12 @@ helps['containerapp job execution list'] = """ type: command short-summary: Get list of all executions of a Container App Job. + long-summary: Automatically follows pagination to return all available executions. examples: - name: List of all executions of a Container App Job. text: az containerapp job execution list -n my-containerapp-job -g MyResourceGroup + - name: List all executions using a requested page size of 1. + text: az containerapp job execution list -n my-containerapp-job -g MyResourceGroup --page-size 1 """ helps['containerapp job execution show'] = """ diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/_params.py b/src/azure-cli/azure/cli/command_modules/containerapp/_params.py index b6c5c68ebce..cabf6b099df 100644 --- a/src/azure-cli/azure/cli/command_modules/containerapp/_params.py +++ b/src/azure-cli/azure/cli/command_modules/containerapp/_params.py @@ -482,6 +482,10 @@ def load_arguments(self, _): c.argument('name', id_part=None) c.argument('job_execution_name', help='name of the specific job execution.') + with self.argument_context('containerapp job execution list') as c: + c.argument('page_size', type=int, help='Requested number of executions per ARM page. Must be non-negative. ' + 'Omit or use 0 for the service default. The service may cap the page size. All pages are returned.') + with self.argument_context('containerapp job secret') as c: c.argument('secrets', nargs='+', options_list=['--secrets', '-s'], help="A list of secret(s) for the container app job. Space-separated values in 'key=value' or 'key=keyvaultref:keyvaulturl,identityref:identity' format (where 'key' cannot be longer than 20 characters).") c.argument('name', id_part=None, help="The name of the container app job for which the secret needs to be retrieved.") diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/custom.py b/src/azure-cli/azure/cli/command_modules/containerapp/custom.py index 8c479bded7e..37978a2f128 100644 --- a/src/azure-cli/azure/cli/command_modules/containerapp/custom.py +++ b/src/azure-cli/azure/cli/command_modules/containerapp/custom.py @@ -1651,9 +1651,13 @@ def stop_containerappsjob(cmd, resource_group_name, name, job_execution_name=Non handle_raw_exception(e) -def listexecution_containerappsjob(cmd, resource_group_name, name): +def listexecution_containerappsjob(cmd, resource_group_name, name, page_size=None): + if page_size is not None and page_size < 0: + raise InvalidArgumentValueError("--page-size must be a non-negative integer.") + try: - executions = ContainerAppsJobClient.get_executions(cmd=cmd, resource_group_name=resource_group_name, name=name) + executions = ContainerAppsJobClient.get_executions(cmd=cmd, resource_group_name=resource_group_name, name=name, + page_size=page_size) return executions['value'] except CLIError as e: handle_raw_exception(e) diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/tests/latest/test_containerappjob_execution_pagination.py b/src/azure-cli/azure/cli/command_modules/containerapp/tests/latest/test_containerappjob_execution_pagination.py new file mode 100644 index 00000000000..ac06f7a9ebd --- /dev/null +++ b/src/azure-cli/azure/cli/command_modules/containerapp/tests/latest/test_containerappjob_execution_pagination.py @@ -0,0 +1,218 @@ +# -------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License.txt in the project root for license information. +# -------------------------------------------------------------------------------------------- + +from copy import deepcopy +import unittest +from unittest.mock import Mock, call, patch + +from azure.cli.command_modules.containerapp._clients import ContainerAppsJobClient +from azure.cli.command_modules.containerapp.custom import listexecution_containerappsjob +from azure.cli.core.azclierror import AzureResponseError, CLIInternalError, HTTPError, InvalidArgumentValueError + + +def _response(payload): + response = Mock() + response.json.return_value = deepcopy(payload) + return response + + +@patch("azure.cli.command_modules.containerapp._clients.send_raw_request") +class ContainerAppJobExecutionPaginationTests(unittest.TestCase): + def setUp(self): + self.cmd = Mock() + self.endpoint = "https://management.azure.com/" + self.cmd.cli_ctx.cloud.endpoints.resource_manager = self.endpoint + self.path = ( + "/subscriptions/sub-id/resourceGroups/test-rg/providers/Microsoft.App/jobs/test-job/executions" + ) + self.url = self.endpoint.rstrip("/") + self.path + "?api-version=" + ContainerAppsJobClient.api_version + subscription = patch( + "azure.cli.command_modules.containerapp._clients.get_subscription_id", return_value="sub-id" + ) + subscription.start() + self.addCleanup(subscription.stop) + + def _get_executions(self, page_size=None): + return ContainerAppsJobClient.get_executions( + cmd=self.cmd, resource_group_name="test-rg", name="test-job", page_size=page_size + ) + + def _list_executions(self, page_size=None): + return listexecution_containerappsjob( + cmd=self.cmd, resource_group_name="test-rg", name="test-job", page_size=page_size + ) + + def test_containerapp_job_executions_single_page(self, request): + executions = [{"name": "execution-1"}] + for next_link in ("missing", None, ""): + with self.subTest(next_link=next_link): + payload = {"value": executions, "metadata": "preserved"} + if next_link != "missing": + payload["nextLink"] = next_link + request.reset_mock() + request.return_value = _response(payload) + + self.assertEqual(self._get_executions(), payload) + request.assert_called_once_with(self.cmd.cli_ctx, "GET", self.url) + + request.reset_mock() + request.return_value = _response(payload) + self.assertEqual(self._list_executions(), executions) + request.assert_called_once_with(self.cmd.cli_ctx, "GET", self.url) + + def test_containerapp_job_executions_page_size_only_on_initial_request(self, request): + next_url = self.url + "&$skipToken=a%2Fb%2Bc%3D%3D" + executions = [{"name": "execution-1"}, {"name": "execution-2"}] + for page_size in (0, 1, 20): + for client in (True, False): + with self.subTest(page_size=page_size, client=client): + request.reset_mock() + request.side_effect = [ + _response({"value": executions[:1], "nextLink": next_url}), + _response({"value": executions[1:]}), + ] + + result = self._get_executions(page_size) if client else self._list_executions(page_size) + + self.assertEqual(result["value"] if client else result, executions) + self.assertEqual(request.call_args_list, [ + call(self.cmd.cli_ctx, "GET", self.url + f"&pageSize={page_size}"), + call(self.cmd.cli_ctx, "GET", next_url), + ]) + + def test_containerapp_job_executions_negative_page_size(self, request): + with self.assertRaisesRegex(InvalidArgumentValueError, "--page-size must be a non-negative integer"): + self._list_executions(page_size=-1) + request.assert_not_called() + + def test_containerapp_job_executions_three_pages_in_server_order(self, request): + executions = [{"name": f"execution-{index}"} for index in range(45, 0, -1)] + second_url = self.url + "&$skipToken=page-2" + third_url = self.url + "&$skipToken=page-3" + pages = [ + {"value": executions[:20], "nextLink": second_url, "metadata": "preserved"}, + {"value": executions[20:40], "nextLink": third_url}, + {"value": executions[40:]}, + ] + for client in (True, False): + with self.subTest(client=client): + request.reset_mock() + request.side_effect = [_response(page) for page in pages] + + result = self._get_executions() if client else self._list_executions() + + if client: + self.assertIsInstance(result, dict) + self.assertEqual(result["metadata"], "preserved") + self.assertIsNone(result.get("nextLink")) + result = result["value"] + self.assertIsInstance(result, list) + self.assertEqual(len(result), 45) + self.assertEqual(result, executions) + self.assertEqual(request.call_args_list, [ + call(self.cmd.cli_ctx, "GET", self.url), + call(self.cmd.cli_ctx, "GET", second_url), + call(self.cmd.cli_ctx, "GET", third_url), + ]) + + def test_containerapp_job_executions_empty_page_with_next_link(self, request): + second_url = self.url + "&$skipToken=page-2" + third_url = self.url + "&$skipToken=page-3" + for empty_page in (0, 1): + with self.subTest(empty_page=empty_page): + executions = [{"name": "execution-1"}, {"name": "execution-2"}] + values = [[executions[0]], [executions[1]]] + values.insert(empty_page, []) + pages = [ + {"value": values[0], "nextLink": second_url}, + {"value": values[1], "nextLink": third_url}, + {"value": values[2]}, + ] + request.reset_mock() + request.side_effect = [_response(page) for page in pages] + + self.assertEqual(self._list_executions(), executions) + self.assertEqual(request.call_count, 3) + + def test_containerapp_job_executions_preserves_encoded_continuation_url(self, request): + next_url = ( + self.endpoint.rstrip("/") + self.path + + "?api-version=2025-07-01&$skipToken=a%2Fb%2Bc%3D%3D%26d%3Fe%252F" + "&filter=status%20eq%20%27Succeeded%27&pageSize=20" + ) + executions = [{"name": "execution-1"}, {"name": "execution-2"}] + request.side_effect = [ + _response({"value": executions[:1], "nextLink": next_url}), + _response({"value": executions[1:]}), + ] + + self.assertEqual(self._list_executions(), executions) + self.assertEqual(request.call_args_list, [ + call(self.cmd.cli_ctx, "GET", self.url), + call(self.cmd.cli_ctx, "GET", next_url), + ]) + + def test_containerapp_job_executions_later_page_failure(self, request): + second_url = self.url + "&$skipToken=page-2" + third_url = self.url + "&$skipToken=page-3" + error = HTTPError( + 'Internal Server Error({"error":{"code":"InternalServerError","message":"Later page failed"}})', + Mock(status_code=500), + ) + for failure_page in (2, 3): + for client in (True, False): + with self.subTest(failure_page=failure_page, client=client): + responses = [ + _response({"value": [{"name": "execution-1"}], "nextLink": second_url}), + ] + if failure_page == 3: + responses.append( + _response({"value": [{"name": "execution-2"}], "nextLink": third_url}) + ) + request.reset_mock() + request.side_effect = responses + [error] + + if client: + with self.assertRaises(HTTPError) as raised: + self._get_executions() + self.assertIs(raised.exception, error) + else: + with self.assertRaisesRegex(CLIInternalError, r"\(InternalServerError\) Later page failed"): + self._list_executions() + self.assertEqual(request.call_count, failure_page) + + def test_containerapp_job_executions_active_cloud_endpoint(self, request): + for endpoint in ("https://management.usgovcloudapi.net/", "https://management.chinacloudapi.cn/"): + with self.subTest(endpoint=endpoint): + self.cmd.cli_ctx.cloud.endpoints.resource_manager = endpoint + first_url = endpoint.rstrip("/") + self.path + "?api-version=" + ContainerAppsJobClient.api_version + next_url = first_url + "&$skipToken=page-2" + request.reset_mock() + request.side_effect = [ + _response({"value": [], "nextLink": next_url}), + _response({"value": [{"name": "execution-1"}]}), + ] + + self.assertEqual(self._list_executions(), [{"name": "execution-1"}]) + self.assertEqual(request.call_args_list, [ + call(self.cmd.cli_ctx, "GET", first_url), + call(self.cmd.cli_ctx, "GET", next_url), + ]) + + def test_containerapp_job_executions_rejects_non_arm_continuation(self, request): + for endpoint in ( + "https://management.azure.com.example.org/", + "https://management.azure.com@example.org/", + "http://management.azure.com/", + "https://management.azure.com:8443/", + "https://graph.microsoft.com/", + ): + with self.subTest(endpoint=endpoint): + request.reset_mock() + request.return_value = _response({"value": [], "nextLink": endpoint + "executions"}) + + with self.assertRaisesRegex(AzureResponseError, "expected the active cloud's ARM endpoint"): + self._list_executions() + request.assert_called_once_with(self.cmd.cli_ctx, "GET", self.url) From 5161b520b0ea590b0a85595d38b7e96dc6dc7e6c Mon Sep 17 00:00:00 2001 From: Korede Okelowo Date: Wed, 14 Oct 2026 17:11:12 -0500 Subject: [PATCH 2/3] Containerapp: Fix execution list continuation indentation Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 445d57df-fd66-4c9d-b6a0-ee9c14ca213d --- .../azure/cli/command_modules/containerapp/custom.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/custom.py b/src/azure-cli/azure/cli/command_modules/containerapp/custom.py index 37978a2f128..d51dad67cab 100644 --- a/src/azure-cli/azure/cli/command_modules/containerapp/custom.py +++ b/src/azure-cli/azure/cli/command_modules/containerapp/custom.py @@ -1656,8 +1656,8 @@ def listexecution_containerappsjob(cmd, resource_group_name, name, page_size=Non raise InvalidArgumentValueError("--page-size must be a non-negative integer.") try: - executions = ContainerAppsJobClient.get_executions(cmd=cmd, resource_group_name=resource_group_name, name=name, - page_size=page_size) + executions = ContainerAppsJobClient.get_executions( + cmd=cmd, resource_group_name=resource_group_name, name=name, page_size=page_size) return executions['value'] except CLIError as e: handle_raw_exception(e) From 724d6c4242d6409f24230385396357b1f591f9b2 Mon Sep 17 00:00:00 2001 From: Korede Okelowo Date: Thu, 15 Oct 2026 13:52:48 -0500 Subject: [PATCH 3/3] Containerapp: Remove undocumented execution page-size option Keep automatic ARM continuation handling without exposing an undocumented query parameter. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 445d57df-fd66-4c9d-b6a0-ee9c14ca213d --- .../command_modules/containerapp/_clients.py | 5 +-- .../cli/command_modules/containerapp/_help.py | 2 - .../command_modules/containerapp/_params.py | 4 -- .../command_modules/containerapp/custom.py | 7 +--- ...st_containerappjob_execution_pagination.py | 37 +++---------------- 5 files changed, 9 insertions(+), 46 deletions(-) diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/_clients.py b/src/azure-cli/azure/cli/command_modules/containerapp/_clients.py index 224dd5d988a..8945e67e7e4 100644 --- a/src/azure-cli/azure/cli/command_modules/containerapp/_clients.py +++ b/src/azure-cli/azure/cli/command_modules/containerapp/_clients.py @@ -1043,7 +1043,7 @@ def stop_job(cls, cmd, resource_group_name, name, job_execution_name): return r @classmethod - def get_executions(cls, cmd, resource_group_name, name, page_size=None): + def get_executions(cls, cmd, resource_group_name, name): management_hostname = cmd.cli_ctx.cloud.endpoints.resource_manager sub_id = get_subscription_id(cmd.cli_ctx) url_fmt = "{}/subscriptions/{}/resourceGroups/{}/providers/Microsoft.App/jobs/{}/executions?api-version={}" @@ -1054,9 +1054,6 @@ def get_executions(cls, cmd, resource_group_name, name, page_size=None): name, cls.api_version) - if page_size is not None: - request_url += f"&pageSize={page_size}" - r = send_raw_request(cmd.cli_ctx, "GET", request_url) response = r.json() page = response diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/_help.py b/src/azure-cli/azure/cli/command_modules/containerapp/_help.py index c42811d00e3..a42d9c85d12 100644 --- a/src/azure-cli/azure/cli/command_modules/containerapp/_help.py +++ b/src/azure-cli/azure/cli/command_modules/containerapp/_help.py @@ -770,8 +770,6 @@ examples: - name: List of all executions of a Container App Job. text: az containerapp job execution list -n my-containerapp-job -g MyResourceGroup - - name: List all executions using a requested page size of 1. - text: az containerapp job execution list -n my-containerapp-job -g MyResourceGroup --page-size 1 """ helps['containerapp job execution show'] = """ diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/_params.py b/src/azure-cli/azure/cli/command_modules/containerapp/_params.py index cabf6b099df..b6c5c68ebce 100644 --- a/src/azure-cli/azure/cli/command_modules/containerapp/_params.py +++ b/src/azure-cli/azure/cli/command_modules/containerapp/_params.py @@ -482,10 +482,6 @@ def load_arguments(self, _): c.argument('name', id_part=None) c.argument('job_execution_name', help='name of the specific job execution.') - with self.argument_context('containerapp job execution list') as c: - c.argument('page_size', type=int, help='Requested number of executions per ARM page. Must be non-negative. ' - 'Omit or use 0 for the service default. The service may cap the page size. All pages are returned.') - with self.argument_context('containerapp job secret') as c: c.argument('secrets', nargs='+', options_list=['--secrets', '-s'], help="A list of secret(s) for the container app job. Space-separated values in 'key=value' or 'key=keyvaultref:keyvaulturl,identityref:identity' format (where 'key' cannot be longer than 20 characters).") c.argument('name', id_part=None, help="The name of the container app job for which the secret needs to be retrieved.") diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/custom.py b/src/azure-cli/azure/cli/command_modules/containerapp/custom.py index d51dad67cab..a3b97b0e9da 100644 --- a/src/azure-cli/azure/cli/command_modules/containerapp/custom.py +++ b/src/azure-cli/azure/cli/command_modules/containerapp/custom.py @@ -1651,13 +1651,10 @@ def stop_containerappsjob(cmd, resource_group_name, name, job_execution_name=Non handle_raw_exception(e) -def listexecution_containerappsjob(cmd, resource_group_name, name, page_size=None): - if page_size is not None and page_size < 0: - raise InvalidArgumentValueError("--page-size must be a non-negative integer.") - +def listexecution_containerappsjob(cmd, resource_group_name, name): try: executions = ContainerAppsJobClient.get_executions( - cmd=cmd, resource_group_name=resource_group_name, name=name, page_size=page_size) + cmd=cmd, resource_group_name=resource_group_name, name=name) return executions['value'] except CLIError as e: handle_raw_exception(e) diff --git a/src/azure-cli/azure/cli/command_modules/containerapp/tests/latest/test_containerappjob_execution_pagination.py b/src/azure-cli/azure/cli/command_modules/containerapp/tests/latest/test_containerappjob_execution_pagination.py index ac06f7a9ebd..8d1e05f0752 100644 --- a/src/azure-cli/azure/cli/command_modules/containerapp/tests/latest/test_containerappjob_execution_pagination.py +++ b/src/azure-cli/azure/cli/command_modules/containerapp/tests/latest/test_containerappjob_execution_pagination.py @@ -9,7 +9,7 @@ from azure.cli.command_modules.containerapp._clients import ContainerAppsJobClient from azure.cli.command_modules.containerapp.custom import listexecution_containerappsjob -from azure.cli.core.azclierror import AzureResponseError, CLIInternalError, HTTPError, InvalidArgumentValueError +from azure.cli.core.azclierror import AzureResponseError, CLIInternalError, HTTPError def _response(payload): @@ -34,14 +34,14 @@ def setUp(self): subscription.start() self.addCleanup(subscription.stop) - def _get_executions(self, page_size=None): + def _get_executions(self): return ContainerAppsJobClient.get_executions( - cmd=self.cmd, resource_group_name="test-rg", name="test-job", page_size=page_size + cmd=self.cmd, resource_group_name="test-rg", name="test-job" ) - def _list_executions(self, page_size=None): + def _list_executions(self): return listexecution_containerappsjob( - cmd=self.cmd, resource_group_name="test-rg", name="test-job", page_size=page_size + cmd=self.cmd, resource_group_name="test-rg", name="test-job" ) def test_containerapp_job_executions_single_page(self, request): @@ -62,31 +62,6 @@ def test_containerapp_job_executions_single_page(self, request): self.assertEqual(self._list_executions(), executions) request.assert_called_once_with(self.cmd.cli_ctx, "GET", self.url) - def test_containerapp_job_executions_page_size_only_on_initial_request(self, request): - next_url = self.url + "&$skipToken=a%2Fb%2Bc%3D%3D" - executions = [{"name": "execution-1"}, {"name": "execution-2"}] - for page_size in (0, 1, 20): - for client in (True, False): - with self.subTest(page_size=page_size, client=client): - request.reset_mock() - request.side_effect = [ - _response({"value": executions[:1], "nextLink": next_url}), - _response({"value": executions[1:]}), - ] - - result = self._get_executions(page_size) if client else self._list_executions(page_size) - - self.assertEqual(result["value"] if client else result, executions) - self.assertEqual(request.call_args_list, [ - call(self.cmd.cli_ctx, "GET", self.url + f"&pageSize={page_size}"), - call(self.cmd.cli_ctx, "GET", next_url), - ]) - - def test_containerapp_job_executions_negative_page_size(self, request): - with self.assertRaisesRegex(InvalidArgumentValueError, "--page-size must be a non-negative integer"): - self._list_executions(page_size=-1) - request.assert_not_called() - def test_containerapp_job_executions_three_pages_in_server_order(self, request): executions = [{"name": f"execution-{index}"} for index in range(45, 0, -1)] second_url = self.url + "&$skipToken=page-2" @@ -140,7 +115,7 @@ def test_containerapp_job_executions_preserves_encoded_continuation_url(self, re next_url = ( self.endpoint.rstrip("/") + self.path + "?api-version=2025-07-01&$skipToken=a%2Fb%2Bc%3D%3D%26d%3Fe%252F" - "&filter=status%20eq%20%27Succeeded%27&pageSize=20" + "&filter=status%20eq%20%27Succeeded%27" ) executions = [{"name": "execution-1"}, {"name": "execution-2"}] request.side_effect = [