From 542a637a539128f4117635bbfa3a6344d3490d08 Mon Sep 17 00:00:00 2001 From: Gavin Aguiar Date: Thu, 16 Mar 2023 11:38:34 -0500 Subject: [PATCH 1/5] Added durable functions stein tests --- .../durable_functions_stein/function_app.py | 28 +++++++++++++++++++ tests/endtoend/test_durable_functions.py | 8 ++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/endtoend/durable_functions/durable_functions_stein/function_app.py diff --git a/tests/endtoend/durable_functions/durable_functions_stein/function_app.py b/tests/endtoend/durable_functions/durable_functions_stein/function_app.py new file mode 100644 index 000000000..14f05a2d4 --- /dev/null +++ b/tests/endtoend/durable_functions/durable_functions_stein/function_app.py @@ -0,0 +1,28 @@ +import azure.functions as func +import azure.durable_functions as df +import logging + +app = df.DFApp(http_auth_level=func.AuthLevel.ANONYMOUS) + + +@app.orchestration_trigger(context_name="context") +def durablefunctionsorchestrator(context): + result1 = yield context.call_activity('Hello', "Tokyo") + result2 = yield context.call_activity('Hello', "Seattle") + result3 = yield context.call_activity('Hello', "London") + return [result1, result2, result3] + + +@app.route(route="orchestrators/{functionName}", + auth_level=func.AuthLevel.ANONYMOUS) +@app.durable_client_input(client_name="client") +async def durable_client(req: func.HttpRequest, client) -> func.HttpResponse: + instance_id = await client.start_new(req.route_params["functionName"], None, + None) + logging.info(f"Started orchestration with ID = '{instance_id}'.") + return client.create_check_status_response(req, instance_id) + + +@app.activity_trigger(input_name="name") +def hello(name: str) -> str: + return f"Hello {name}!" diff --git a/tests/endtoend/test_durable_functions.py b/tests/endtoend/test_durable_functions.py index 76e75ab65..d55de62d3 100644 --- a/tests/endtoend/test_durable_functions.py +++ b/tests/endtoend/test_durable_functions.py @@ -13,3 +13,11 @@ def test_durable(self): r = self.webhost.request('GET', 'orchestrators/DurableFunctionsOrchestrator') self.assertEqual(r.status_code, 202) + + +class TestDurableFunctionsStein(TestDurableFunctions): + + @classmethod + def get_script_dir(cls): + return testutils.E2E_TESTS_FOLDER / 'durable_functions' / \ + 'durable_functions_stein' From eaaa8402c1ce611fb5c99811ee72dc7f10c4fb2a Mon Sep 17 00:00:00 2001 From: Gavin Aguiar Date: Fri, 14 Apr 2023 13:59:07 -0500 Subject: [PATCH 2/5] Fixed durable tests --- setup.py | 2 +- .../durable_functions/Hello/__init__.py | 3 +- .../durable_functions/Hello/function.json | 8 ----- tests/endtoend/test_durable_functions.py | 31 +++++++++++++++++++ tests/utils/testutils.py | 3 -- 5 files changed, 33 insertions(+), 14 deletions(-) diff --git a/setup.py b/setup.py index 3ea270ae0..01c53bea9 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ Include="Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator" Version="1.1.3" /> + Version="2.9.8" /> """ diff --git a/tests/endtoend/durable_functions/Hello/__init__.py b/tests/endtoend/durable_functions/Hello/__init__.py index e5c77a9fc..15cee6a5b 100644 --- a/tests/endtoend/durable_functions/Hello/__init__.py +++ b/tests/endtoend/durable_functions/Hello/__init__.py @@ -6,6 +6,5 @@ # - add azure-functions-durable to requirements.txt # - run pip install -r requirements.txt -def main(name: str, blob) -> str: - blob.set("test-durable") +def main(name: str) -> str: return f"Hello {name}!" diff --git a/tests/endtoend/durable_functions/Hello/function.json b/tests/endtoend/durable_functions/Hello/function.json index 5f3ed9ad9..1b03f1100 100644 --- a/tests/endtoend/durable_functions/Hello/function.json +++ b/tests/endtoend/durable_functions/Hello/function.json @@ -5,14 +5,6 @@ "name": "name", "type": "activityTrigger", "direction": "in" - }, - { - "type": "blob", - "direction": "out", - "name": "blob", - "path": "python-worker-tests/test-return1.txt", - "connection": "AzureWebJobsStorage" } - ] } diff --git a/tests/endtoend/test_durable_functions.py b/tests/endtoend/test_durable_functions.py index d55de62d3..37ffad9fb 100644 --- a/tests/endtoend/test_durable_functions.py +++ b/tests/endtoend/test_durable_functions.py @@ -1,10 +1,31 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +import json +import time +from unittest.mock import patch + +import requests from tests.utils import testutils +import os class TestDurableFunctions(testutils.WebHostTestCase): + @classmethod + def setUpClass(cls): + os_environ = os.environ.copy() + # Webhook for durable functions + os.environ['WEBSITE_HOSTNAME'] = f'http:' + + cls._patch_environ = patch.dict('os.environ', os_environ) + cls._patch_environ.start() + super().setUpClass() + + @classmethod + def tearDownClass(self): + super().tearDownClass() + self._patch_environ.stop() + @classmethod def get_script_dir(cls): return testutils.E2E_TESTS_FOLDER / 'durable_functions' @@ -13,6 +34,16 @@ def test_durable(self): r = self.webhost.request('GET', 'orchestrators/DurableFunctionsOrchestrator') self.assertEqual(r.status_code, 202) + content = json.loads(r.content) + + time.sleep(2) # wait for the activity to complete + status = requests.get(content['statusQueryGetUri']) + self.assertEqual(status.status_code, 200) + + status_content = json.loads(status.content) + self.assertEqual(status_content['runtimeStatus'], 'Completed') + self.assertEqual(status_content['output'], + ['Hello Tokyo!', 'Hello Seattle!', 'Hello London!']) class TestDurableFunctionsStein(TestDurableFunctions): diff --git a/tests/utils/testutils.py b/tests/utils/testutils.py index 0df17e2d8..e018e5b4d 100644 --- a/tests/utils/testutils.py +++ b/tests/utils/testutils.py @@ -787,9 +787,6 @@ def popen_webhost(*, stdout, stderr, script_root=FUNCS_PATH, port=None): hostexe_args = [] os.environ['AzureWebJobsFeatureFlags'] = 'EnableWorkerIndexing' - # webhook for durable tests - os.environ['WEBSITE_HOSTNAME'] = f'http://*:{port}' - # If we want to use core-tools coretools_exe = os.environ.get('CORE_TOOLS_EXE_PATH') if coretools_exe: From 9183855434fde88ca7d0f9241931f1831db44753 Mon Sep 17 00:00:00 2001 From: Gavin Aguiar Date: Fri, 14 Apr 2023 14:09:25 -0500 Subject: [PATCH 3/5] Updated durable task extension version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 01c53bea9..8d6475865 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ Include="Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator" Version="1.1.3" /> + Version="2.9.3" /> """ From 5684cb6fd916349c40dca3f6ed893766e677c862 Mon Sep 17 00:00:00 2001 From: Gavin Aguiar Date: Fri, 14 Apr 2023 14:34:10 -0500 Subject: [PATCH 4/5] Flake8 fix --- tests/endtoend/test_durable_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/endtoend/test_durable_functions.py b/tests/endtoend/test_durable_functions.py index 37ffad9fb..fae68ec65 100644 --- a/tests/endtoend/test_durable_functions.py +++ b/tests/endtoend/test_durable_functions.py @@ -15,7 +15,7 @@ class TestDurableFunctions(testutils.WebHostTestCase): def setUpClass(cls): os_environ = os.environ.copy() # Webhook for durable functions - os.environ['WEBSITE_HOSTNAME'] = f'http:' + os.environ['WEBSITE_HOSTNAME'] = 'http:' cls._patch_environ = patch.dict('os.environ', os_environ) cls._patch_environ.start() From 92d87ea0f27c9473d60247473f7d1c4e504bd082 Mon Sep 17 00:00:00 2001 From: Gavin Aguiar Date: Thu, 27 Apr 2023 14:32:02 -0500 Subject: [PATCH 5/5] Flake8 fixes --- tests/endtoend/test_durable_functions.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/endtoend/test_durable_functions.py b/tests/endtoend/test_durable_functions.py index 7376a8444..4bb5ed433 100644 --- a/tests/endtoend/test_durable_functions.py +++ b/tests/endtoend/test_durable_functions.py @@ -41,7 +41,6 @@ def get_libraries_to_install(cls): def get_environment_variables(cls): return cls.env_variables - @classmethod def get_script_dir(cls): return testutils.E2E_TESTS_FOLDER / 'durable_functions' @@ -49,7 +48,7 @@ def get_script_dir(cls): def test_durable(self): r = self.webhost.request('GET', 'orchestrators/DurableFunctionsOrchestrator') - time.sleep(4) # wait for the activity to complete + time.sleep(4) # wait for the activity to complete self.assertEqual(r.status_code, 202) content = json.loads(r.content)