diff --git a/eng/templates/official/jobs/ci-core-tools-tests.yml b/eng/templates/official/jobs/ci-core-tools-tests.yml index 32b8181a8..35e3b8775 100644 --- a/eng/templates/official/jobs/ci-core-tools-tests.yml +++ b/eng/templates/official/jobs/ci-core-tools-tests.yml @@ -37,6 +37,4 @@ jobs: AzureWebJobsEventHubConnectionString: $(LinuxEventHubConnectionString311) AzureWebJobsServiceBusConnectionString: $(LinuxServiceBusConnectionString311) AzureWebJobsSqlConnectionString: $(LinuxSqlConnectionString311) - AzureWebJobsEventGridTopicUri: $(LinuxEventGridTopicUriString311) - AzureWebJobsEventGridConnectionKey: $(LinuxEventGridConnectionKeyString311) displayName: 'Running Python Language Worker E2E Tests' diff --git a/eng/templates/official/jobs/ci-custom-image-tests.yml b/eng/templates/official/jobs/ci-custom-image-tests.yml index 481f4d10c..66a890dec 100644 --- a/eng/templates/official/jobs/ci-custom-image-tests.yml +++ b/eng/templates/official/jobs/ci-custom-image-tests.yml @@ -36,7 +36,5 @@ jobs: AzureWebJobsEventHubConnectionString: $(LinuxEventHubConnectionString311) AzureWebJobsServiceBusConnectionString: $(LinuxServiceBusConnectionString311) AzureWebJobsSqlConnectionString: $(LinuxSqlConnectionString311) - AzureWebJobsEventGridTopicUri: $(LinuxEventGridTopicUriString311) - AzureWebJobsEventGridConnectionKey: $(LinuxEventGridConnectionKeyString311) workingDirectory: $(Build.SourcesDirectory)/${{ parameters.PROJECT_DIRECTORY }} displayName: "Running Python DockerCustom tests" \ No newline at end of file diff --git a/eng/templates/official/jobs/ci-docker-dedicated-tests.yml b/eng/templates/official/jobs/ci-docker-dedicated-tests.yml index ba25ec208..1aebc5546 100644 --- a/eng/templates/official/jobs/ci-docker-dedicated-tests.yml +++ b/eng/templates/official/jobs/ci-docker-dedicated-tests.yml @@ -16,20 +16,14 @@ jobs: PYTHON_VERSION: '3.10' STORAGE_CONNECTION: $(LinuxStorageConnectionString310) SQL_CONNECTION: $(LinuxSqlConnectionString310) - EVENTGRID_URI: $(LinuxEventGridTopicUriString310) - EVENTGRID_CONNECTION: $(LinuxEventGridConnectionKeyString310) Python311: PYTHON_VERSION: '3.11' STORAGE_CONNECTION: $(LinuxStorageConnectionString311) SQL_CONNECTION: $(LinuxSqlConnectionString311) - EVENTGRID_URI: $(LinuxEventGridTopicUriString311) - EVENTGRID_CONNECTION: $(LinuxEventGridConnectionKeyString311) Python312: PYTHON_VERSION: '3.12' STORAGE_CONNECTION: $(LinuxStorageConnectionString312) SQL_CONNECTION: $(LinuxSqlConnectionString312) - EVENTGRID_URI: $(LinuxEventGridTopicUriString312) - EVENTGRID_CONNECTION: $(LinuxEventGridConnectionKeyString312) steps: - task: PipAuthenticate@1 @@ -54,7 +48,5 @@ jobs: AzureWebJobsStorage: $(STORAGE_CONNECTION) STORAGE_CONNECTION: $(STORAGE_CONNECTION) AzureWebJobsSqlConnectionString: $(SQL_CONNECTION) - AzureWebJobsEventGridTopicUri: $(EVENTGRID_URI) - AzureWebJobsEventGridConnectionKey: $(EVENTGRID_CONNECTION) workingDirectory: $(Build.SourcesDirectory)/${{ parameters.PROJECT_DIRECTORY }} displayName: "Running $(PYTHON_VERSION) Docker Dedicated tests" \ No newline at end of file diff --git a/eng/templates/official/jobs/ci-e2e-tests.yml b/eng/templates/official/jobs/ci-e2e-tests.yml index eb9b49c7b..58e033d1b 100644 --- a/eng/templates/official/jobs/ci-e2e-tests.yml +++ b/eng/templates/official/jobs/ci-e2e-tests.yml @@ -17,26 +17,18 @@ jobs: PYTHON_VERSION: '3.10' STORAGE_CONNECTION: $(LinuxStorageConnectionString310) SQL_CONNECTION: $(LinuxSqlConnectionString310) - EVENTGRID_URI: $(LinuxEventGridTopicUriString310) - EVENTGRID_CONNECTION: $(LinuxEventGridConnectionKeyString310) Python311: PYTHON_VERSION: '3.11' STORAGE_CONNECTION: $(LinuxStorageConnectionString311) SQL_CONNECTION: $(LinuxSqlConnectionString311) - EVENTGRID_URI: $(LinuxEventGridTopicUriString311) - EVENTGRID_CONNECTION: $(LinuxEventGridConnectionKeyString311) Python312: PYTHON_VERSION: '3.12' STORAGE_CONNECTION: $(LinuxStorageConnectionString312) SQL_CONNECTION: $(LinuxSqlConnectionString312) - EVENTGRID_URI: $(LinuxEventGridTopicUriString312) - EVENTGRID_CONNECTION: $(LinuxEventGridConnectionKeyString312) Python313: PYTHON_VERSION: '3.13' STORAGE_CONNECTION: $(LinuxStorageConnectionString37) SQL_CONNECTION: $(LinuxSqlConnectionString37) - EVENTGRID_URI: $(LinuxEventGridTopicUriString37) - EVENTGRID_CONNECTION: $(LinuxEventGridConnectionKeyString37) Python314: PYTHON_VERSION: '3.14' STORAGE_CONNECTION: $(LinuxStorageConnectionString38) @@ -204,8 +196,6 @@ jobs: AzureWebJobsStorage: $(STORAGE_CONNECTION) STORAGE_CONNECTION: $(STORAGE_CONNECTION) AzureWebJobsSqlConnectionString: $(SQL_CONNECTION) - AzureWebJobsEventGridTopicUri: $(EVENTGRID_URI) - AzureWebJobsEventGridConnectionKey: $(EVENTGRID_CONNECTION) skipTest: $(skipTest) PYAZURE_WEBHOST_DEBUG: true displayName: "Running $(PYTHON_VERSION) Python E2E Tests" diff --git a/workers/tests/endtoend/eventgrid_functions/eventgrid_functions_stein/function_app.py b/workers/tests/emulator_tests/eventgrid_functions/eventgrid_functions_stein/function_app.py similarity index 68% rename from workers/tests/endtoend/eventgrid_functions/eventgrid_functions_stein/function_app.py rename to workers/tests/emulator_tests/eventgrid_functions/eventgrid_functions_stein/function_app.py index 4f2caa97f..97084a4f9 100644 --- a/workers/tests/endtoend/eventgrid_functions/eventgrid_functions_stein/function_app.py +++ b/workers/tests/emulator_tests/eventgrid_functions/eventgrid_functions_stein/function_app.py @@ -1,18 +1,17 @@ import json +import logging from datetime import datetime import azure.functions as func -from azure_functions_worker import logging - app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) -@app.function_name(name="eventGridTrigger") +@app.function_name(name="eventgrid_trigger") @app.event_grid_trigger(arg_name="event") @app.blob_output(arg_name="$return", path="python-worker-tests/test-eventgrid-triggered.txt", - connection="STORAGE_CONNECTION") + connection="AzureWebJobsStorage") def event_grid_trigger(event: func.EventGridEvent) -> str: logging.info("Event grid function is triggered!") return json.dumps({ @@ -52,32 +51,11 @@ def eventgrid_output_binding( return func.HttpResponse(r_value) -@app.function_name(name="eventgrid_output_binding_message_to_blobstore") -@app.queue_trigger(arg_name="msg", queue_name="test-event-grid-storage-queue", - connection="STORAGE_CONNECTION") -@app.blob_output(arg_name="$return", - path="python-worker-tests/test-eventgrid-output-binding.txt", - connection="STORAGE_CONNECTION") -def eventgrid_output_binding_message_to_blobstore( - msg: func.QueueMessage) -> bytes: - return msg.get_body() - - -@app.function_name(name="eventgrid_output_binding_success") -@app.route(route="eventgrid_output_binding_success") -@app.blob_input(arg_name="file", - path="python-worker-tests/test-eventgrid-output-binding.txt", - connection="STORAGE_CONNECTION") -def eventgrid_output_binding_success( - req: func.HttpRequest, file: func.InputStream) -> str: - return file.read().decode('utf-8') - - @app.function_name(name="get_eventgrid_triggered") @app.route(route="get_eventgrid_triggered") @app.blob_input(arg_name="file", path="python-worker-tests/test-eventgrid-triggered.txt", - connection="STORAGE_CONNECTION") + connection="AzureWebJobsStorage") def get_eventgrid_triggered( req: func.HttpRequest, file: func.InputStream) -> str: return file.read().decode('utf-8') diff --git a/workers/tests/endtoend/eventgrid_functions/eventgrid_functions_stein/generic/function_app.py b/workers/tests/emulator_tests/eventgrid_functions/eventgrid_functions_stein/generic/function_app.py similarity index 67% rename from workers/tests/endtoend/eventgrid_functions/eventgrid_functions_stein/generic/function_app.py rename to workers/tests/emulator_tests/eventgrid_functions/eventgrid_functions_stein/generic/function_app.py index 4403e5329..9b88b1513 100644 --- a/workers/tests/endtoend/eventgrid_functions/eventgrid_functions_stein/generic/function_app.py +++ b/workers/tests/emulator_tests/eventgrid_functions/eventgrid_functions_stein/generic/function_app.py @@ -1,19 +1,18 @@ import json +import logging from datetime import datetime import azure.functions as func -from azure_functions_worker import logging - app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) -@app.function_name(name="eventGridTrigger") +@app.function_name(name="eventgrid_trigger") @app.event_grid_trigger(arg_name="event", type="eventGridTrigger") @app.generic_output_binding( arg_name="$return", type="blob", - connection="STORAGE_CONNECTION", + connection="AzureWebJobsStorage", path="python-worker-tests/test-eventgrid-triggered.txt") def event_grid_trigger(event: func.EventGridEvent) -> str: logging.info("Event grid function is triggered!") @@ -57,35 +56,6 @@ def eventgrid_output_binding( return func.HttpResponse(r_value) -@app.function_name(name="eventgrid_output_binding_message_to_blobstore") -@app.generic_trigger(arg_name="msg", - type="queueTrigger", - queue_name="test-event-grid-storage-queue", - connection="STORAGE_CONNECTION") -@app.generic_output_binding( - arg_name="$return", - type="blob", - connection="STORAGE_CONNECTION", - path="python-worker-tests/test-eventgrid-output-binding.txt") -def eventgrid_output_binding_message_to_blobstore( - msg: func.QueueMessage) -> bytes: - return msg.get_body() - - -@app.function_name(name="eventgrid_output_binding_success") -@app.generic_trigger(arg_name="req", type="httpTrigger", - route="eventgrid_output_binding_success") -@app.generic_output_binding(arg_name="$return", type="http") -@app.generic_input_binding( - arg_name="file", - type="blob", - path="python-worker-tests/test-eventgrid-output-binding.txt", - connection="STORAGE_CONNECTION") -def eventgrid_output_binding_success( - req: func.HttpRequest, file: func.InputStream) -> str: - return file.read().decode('utf-8') - - @app.function_name(name="get_eventgrid_triggered") @app.generic_trigger(arg_name="req", type="httpTrigger", route="get_eventgrid_triggered") @@ -94,7 +64,7 @@ def eventgrid_output_binding_success( arg_name="file", type="blob", path="python-worker-tests/test-eventgrid-triggered.txt", - connection="STORAGE_CONNECTION") + connection="AzureWebJobsStorage") def get_eventgrid_triggered( req: func.HttpRequest, file: func.InputStream) -> str: return file.read().decode('utf-8') diff --git a/workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding/__init__.py b/workers/tests/emulator_tests/eventgrid_functions/eventgrid_output_binding/__init__.py similarity index 100% rename from workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding/__init__.py rename to workers/tests/emulator_tests/eventgrid_functions/eventgrid_output_binding/__init__.py diff --git a/workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding/function.json b/workers/tests/emulator_tests/eventgrid_functions/eventgrid_output_binding/function.json similarity index 100% rename from workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding/function.json rename to workers/tests/emulator_tests/eventgrid_functions/eventgrid_output_binding/function.json diff --git a/workers/tests/endtoend/eventgrid_functions/eventgrid_trigger/__init__.py b/workers/tests/emulator_tests/eventgrid_functions/eventgrid_trigger/__init__.py similarity index 100% rename from workers/tests/endtoend/eventgrid_functions/eventgrid_trigger/__init__.py rename to workers/tests/emulator_tests/eventgrid_functions/eventgrid_trigger/__init__.py diff --git a/workers/tests/endtoend/eventgrid_functions/eventgrid_trigger/function.json b/workers/tests/emulator_tests/eventgrid_functions/eventgrid_trigger/function.json similarity index 87% rename from workers/tests/endtoend/eventgrid_functions/eventgrid_trigger/function.json rename to workers/tests/emulator_tests/eventgrid_functions/eventgrid_trigger/function.json index 178c219d1..bf33c7072 100644 --- a/workers/tests/endtoend/eventgrid_functions/eventgrid_trigger/function.json +++ b/workers/tests/emulator_tests/eventgrid_functions/eventgrid_trigger/function.json @@ -11,7 +11,7 @@ "type": "blob", "direction": "out", "name": "$return", - "connection": "STORAGE_CONNECTION", + "connection": "AzureWebJobsStorage", "path": "python-worker-tests/test-eventgrid-triggered.txt" } ] diff --git a/workers/tests/endtoend/eventgrid_functions/get_eventgrid_triggered/function.json b/workers/tests/emulator_tests/eventgrid_functions/get_eventgrid_triggered/function.json similarity index 90% rename from workers/tests/endtoend/eventgrid_functions/get_eventgrid_triggered/function.json rename to workers/tests/emulator_tests/eventgrid_functions/get_eventgrid_triggered/function.json index 7ccc66eca..2c2727754 100644 --- a/workers/tests/endtoend/eventgrid_functions/get_eventgrid_triggered/function.json +++ b/workers/tests/emulator_tests/eventgrid_functions/get_eventgrid_triggered/function.json @@ -11,7 +11,7 @@ "type": "blob", "direction": "in", "name": "file", - "connection": "STORAGE_CONNECTION", + "connection": "AzureWebJobsStorage", "path": "python-worker-tests/test-eventgrid-triggered.txt" }, { diff --git a/workers/tests/endtoend/eventgrid_functions/get_eventgrid_triggered/main.py b/workers/tests/emulator_tests/eventgrid_functions/get_eventgrid_triggered/main.py similarity index 100% rename from workers/tests/endtoend/eventgrid_functions/get_eventgrid_triggered/main.py rename to workers/tests/emulator_tests/eventgrid_functions/get_eventgrid_triggered/main.py diff --git a/workers/tests/emulator_tests/test_eventgrid_functions.py b/workers/tests/emulator_tests/test_eventgrid_functions.py new file mode 100644 index 000000000..33e453bbf --- /dev/null +++ b/workers/tests/emulator_tests/test_eventgrid_functions.py @@ -0,0 +1,241 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import json +import os +import queue +import threading +import time +import uuid +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from urllib.parse import parse_qs, urlparse + +import requests +from tests.utils import testutils + + +_EVENT_GRID_TOPIC_KEY = 'local-event-grid-key' + + +def _create_eventgrid_request_handler(captured_requests): + """Create the local receiver for Event Grid output binding requests. + + The handler records each request's path, query, headers, and JSON body in + a queue for test assertions, then returns a successful response. + """ + + class EventGridRequestHandler(BaseHTTPRequestHandler): + + def do_POST(self): + content_length = int(self.headers.get('Content-Length', 0)) + body = self.rfile.read(content_length) + request_url = urlparse(self.path) + captured_requests.put({ + 'path': request_url.path, + 'query': parse_qs(request_url.query), + 'headers': {key.lower(): value + for key, value in self.headers.items()}, + 'body': json.loads(body), + }) + + self.send_response(200) + self.send_header('Content-Length', '0') + self.end_headers() + + def log_message(self, message_format, *args): + pass + + return EventGridRequestHandler + + +class TestEventGridFunctions(testutils.WebHostTestCase): + + @classmethod + def setUpClass(cls): + cls.eventgrid_requests = queue.Queue() + request_handler = _create_eventgrid_request_handler( + cls.eventgrid_requests) + cls.eventgrid_server = ThreadingHTTPServer( + (testutils.LOCALHOST, 0), request_handler) + cls.eventgrid_server.daemon_threads = True + cls.eventgrid_thread = threading.Thread( + target=cls.eventgrid_server.serve_forever, + daemon=True) + + server_port = cls.eventgrid_server.server_address[1] + os.environ['AzureWebJobsEventGridTopicUri'] = ( + f'http://{testutils.LOCALHOST}:{server_port}/api/events') + os.environ['AzureWebJobsEventGridConnectionKey'] = \ + _EVENT_GRID_TOPIC_KEY + cls.eventgrid_thread.start() + + try: + super().setUpClass() + except Exception: + cls._stop_eventgrid_server() + raise + + @classmethod + def tearDownClass(cls): + try: + if getattr(cls, 'webhost', None) is not None: + super().tearDownClass() + finally: + cls._stop_eventgrid_server() + + @classmethod + def _stop_eventgrid_server(cls): + server = getattr(cls, 'eventgrid_server', None) + if server is not None: + server.shutdown() + server.server_close() + cls.eventgrid_server = None + + server_thread = getattr(cls, 'eventgrid_thread', None) + if server_thread is not None: + server_thread.join(timeout=5) + cls.eventgrid_thread = None + + @classmethod + def get_script_dir(cls): + return testutils.EMULATOR_TESTS_FOLDER / 'eventgrid_functions' + + def eventgrid_webhook_request(self, meth, funcname, *args, **kwargs): + """Send an Event Grid notification to the local Functions host. + + The request targets the Event Grid system webhook with the function + name, test master key, and notification headers required by the host. + """ + + request_method = getattr(requests, meth.lower()) + url = f'{self.webhost._addr}/runtime/webhooks/eventgrid' + params = dict(kwargs.pop('params', {})) + params['functionName'] = funcname + if 'code' not in params: + params['code'] = 'testMasterKey' + headers = dict(kwargs.pop('headers', {})) + headers['aeg-event-type'] = 'Notification' + headers.setdefault('Content-Type', 'application/json') + return request_method(url, *args, params=params, headers=headers, + **kwargs) + + def test_eventgrid_trigger(self): + """Verify the local host processes an Event Grid trigger. + + The test posts an event to the Event Grid webhook, then polls an HTTP + function that reads the triggered function's blob output from Azurite. + It verifies the stored event matches the event that was posted. + """ + + event = { + "topic": "test-topic", + "subject": "test-subject", + "eventType": "Microsoft.Storage.BlobCreated", + "eventTime": "2018-01-01T00:00:00.000000123Z", + "id": str(uuid.uuid4()), + "data": { + "api": "PutBlockList", + "clientRequestId": "2c169f2f-7b3b-4d99-839b-c92a2d25801b", + "requestId": "44d4f022-001e-003c-466b-940cba000000", + "eTag": "0x8D562831044DDD0", + "contentType": "application/octet-stream", + "contentLength": 2248, + "blobType": "BlockBlob", + "ur1": "foo", + "sequencer": "000000000000272D000000000003D60F", + "storageDiagnostics": { + "batchId": "b4229b3a-4d50-4ff4-a9f2-039ccf26efe9" + } + }, + "dataVersion": "", + "metadataVersion": "1" + } + + response = self.eventgrid_webhook_request( + 'POST', 'eventgrid_trigger', json=[event]) + self.assertEqual(response.status_code, 202) + + max_retries = 10 + + for try_no in range(max_retries): + # Allow trigger to fire. + time.sleep(2) + + try: + response = self.webhost.request( + 'GET', 'get_eventgrid_triggered') + self.assertEqual(response.status_code, 200) + + response_data = response.json() + self.assertEqual(response_data['id'], event['id']) + self.assertEqual(response_data['data'], event['data']) + self.assertEqual(response_data['topic'], event['topic']) + self.assertEqual(response_data['subject'], event['subject']) + self.assertEqual( + response_data['event_type'], event['eventType']) + except AssertionError: + if try_no == max_retries - 1: + raise + else: + break + + def test_eventgrid_output_binding(self): + """Verify an Event Grid output binding publishes the expected event. + + The test invokes an HTTP function that emits an Event Grid event, then + reads the resulting POST from the local receiver and validates its + endpoint, content type, and event payload. + """ + + while not self.eventgrid_requests.empty(): + self.eventgrid_requests.get_nowait() + + test_uuid = str(uuid.uuid4()) + expected_response = "Sent event with subject: {}, id: {}, data: {}, " \ + "event_type: {} to EventGrid!".format( + "test-subject", "test-id", + f"{{'test_uuid': '{test_uuid}'}}", + "test-event-1") + expected_final_data = { + 'id': 'test-id', 'subject': 'test-subject', 'dataVersion': '1.0', + 'eventType': 'test-event-1', + 'data': {'test_uuid': test_uuid} + } + + response = self.webhost.request( + 'GET', 'eventgrid_output_binding', + params={'test_uuid': test_uuid}) + self.assertEqual(response.status_code, 200) + self.assertEqual(response.text, expected_response) + + try: + captured_request = self.eventgrid_requests.get(timeout=5) + except queue.Empty: + self.fail('Event Grid output binding did not publish an event') + + self.assertEqual(captured_request['path'], '/api/events') + self.assertIn('api-version', captured_request['query']) + self.assertEqual( + captured_request['headers']['content-type'].split(';')[0], + 'application/json') + + published_events = captured_request['body'] + self.assertEqual(len(published_events), 1) + published_event = published_events[0] + for field, expected_value in expected_final_data.items(): + self.assertEqual(published_event[field], expected_value) + + +class TestEventGridFunctionsStein(TestEventGridFunctions): + + @classmethod + def get_script_dir(cls): + return testutils.EMULATOR_TESTS_FOLDER / 'eventgrid_functions' / \ + 'eventgrid_functions_stein' + + +class TestEventGridFunctionsGeneric(TestEventGridFunctions): + + @classmethod + def get_script_dir(cls): + return testutils.EMULATOR_TESTS_FOLDER / 'eventgrid_functions' / \ + 'eventgrid_functions_stein' / 'generic' diff --git a/workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding_message_to_blobstore/__init__.py b/workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding_message_to_blobstore/__init__.py deleted file mode 100644 index b0d9c3096..000000000 --- a/workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding_message_to_blobstore/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -import azure.functions as func - - -def main(msg: func.QueueMessage) -> bytes: - return msg.get_body() diff --git a/workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding_message_to_blobstore/function.json b/workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding_message_to_blobstore/function.json deleted file mode 100644 index c7935c4c3..000000000 --- a/workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding_message_to_blobstore/function.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "scriptFile": "__init__.py", - "bindings": [ - { - "name": "msg", - "type": "queueTrigger", - "direction": "in", - "queueName": "test-event-grid-storage-queue", - "connection": "STORAGE_CONNECTION" - }, - { - "type": "blob", - "direction": "out", - "name": "$return", - "connection": "STORAGE_CONNECTION", - "path": "python-worker-tests/test-eventgrid-output-binding.txt" - } - ] -} diff --git a/workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding_success/__init__.py b/workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding_success/__init__.py deleted file mode 100644 index 7e9725f0b..000000000 --- a/workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding_success/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -import azure.functions as func - - -def main(req: func.HttpRequest, file: func.InputStream) -> str: - return file.read().decode('utf-8') diff --git a/workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding_success/function.json b/workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding_success/function.json deleted file mode 100644 index 772fe35ea..000000000 --- a/workers/tests/endtoend/eventgrid_functions/eventgrid_output_binding_success/function.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "scriptFile": "__init__.py", - "bindings": [ - { - "type": "httpTrigger", - "direction": "in", - "name": "req", - "authLevel": "anonymous" - }, - { - "type": "blob", - "direction": "in", - "name": "file", - "connection": "STORAGE_CONNECTION", - "path": "python-worker-tests/test-eventgrid-output-binding.txt" - }, - { - "type": "http", - "direction": "out", - "name": "$return" - } - ] -} diff --git a/workers/tests/endtoend/test_eventgrid_functions.py b/workers/tests/endtoend/test_eventgrid_functions.py deleted file mode 100644 index 6c435f259..000000000 --- a/workers/tests/endtoend/test_eventgrid_functions.py +++ /dev/null @@ -1,175 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. -import time -import unittest -import uuid - -import requests -from tests.utils import testutils -from tests.utils.constants import DEDICATED_DOCKER_TEST -from azure_functions_worker.utils.common import is_envvar_true - - -class TestEventGridFunctions(testutils.WebHostTestCase): - - @classmethod - def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'eventgrid_functions' - - def eventgrid_webhook_request(self, meth, funcname, *args, **kwargs): - request_method = getattr(requests, meth.lower()) - url = f'{self.webhost._addr}/runtime/webhooks/eventgrid' - params = dict(kwargs.pop('params', {})) - params['functionName'] = funcname - if 'code' not in params: - params['code'] = 'testSystemKey' - headers = dict(kwargs.pop('headers', {})) - headers['aeg-event-type'] = 'Notification' - return request_method(url, *args, params=params, headers=headers, - **kwargs) - - @unittest.skip("Run locally. Running on Azure fails with 401/403 as the" - "host does not pick up the SecretKey from the" - "azure_functions_worker.testutils.py.SECRETS_TEMPLATE and" - "because of which we cannot test eventGrid webhook" - "invocation correctly.") - def test_eventgrid_trigger(self): - """test event_grid trigger - - This test calls the eventgrid_trigger function, sends in `data` as body - to the webhook for eventgrid. Once the event is received, the function - writes the data to the blob store. - - Then get_eventgrid_triggered gets called (httpTrigger) and takes blob - input binding, reading the previously written text in blob store - `python-worker-tests/test-eventgrid-triggered.txt`, and then we validate - that the written text matches the one passed to the eventgrid trigger. - """ - data = [{ - "topic": "test-topic", - "subject": "test-subject", - "eventType": "Microsoft.Storage.BlobCreated", - "eventTime": "2018-01-01T00:00:00.000000123Z", - "id": str(uuid.uuid4()), - "data": { - "api": "PutBlockList", - "clientRequestId": "2c169f2f-7b3b-4d99-839b-c92a2d25801b", - "requestId": "44d4f022-001e-003c-466b-940cba000000", - "eTag": "0x8D562831044DDD0", - "contentType": "application/octet-stream", - "contentLength": 2248, - "blobType": "BlockBlob", - "ur1": "foo", - "sequencer": "000000000000272D000000000003D60F", - "storageDiagnostics": { - "batchId": "b4229b3a-4d50-4ff4-a9f2-039ccf26efe9" - } - }, - "dataVersion": "", - "metadataVersion": "1" - }] - - r = self.eventgrid_webhook_request('POST', 'eventgrid_trigger', - json=data) - self.assertEqual(r.status_code, 202) - - max_retries = 10 - - for try_no in range(max_retries): - # Allow trigger to fire. - time.sleep(2) - - try: - # Check that the trigger has fired. - r = self.webhost.request('GET', 'get_eventgrid_triggered') - self.assertEqual(r.status_code, 200) - - response = r.json() - self.assertLessEqual(response.items(), data[0].items()) - except AssertionError: - if try_no == max_retries - 1: - raise - else: - break - - @unittest.skipIf(is_envvar_true(DEDICATED_DOCKER_TEST), - 'EventGrid connection string not available in docker tests') - def test_eventgrid_output_binding(self): - """test event_grid output binding - - This test needs three functions to work. - 1. `eventgrid_output_binding` - 2. `eventgrid_output_binding_message_to_blobstore` - 3. `eventgrid_output_binding_success` - - This test calls the eventgrid_output_binding function, sends in a unique - uuid as `data` in the body to the httpTrigger which sends in that value - in the eventGrid output data. The eventGrid topic is configured to - send the event to a storage queue. - - The second function (`eventgrid_output_binding_message_to_blobstore`) - reads from that storage queue and puts into a blob store. - - The third function (`eventgrid_output_binding_success`) reads the - text from the blob store and compares with the expected result. The - unique uuid should confirm if the message went through correctly to - EventGrid and came back as a blob. - """ - - test_uuid = uuid.uuid4().__str__() - expected_response = "Sent event with subject: {}, id: {}, data: {}, " \ - "event_type: {} to EventGrid!".format( - "test-subject", "test-id", - f"{{'test_uuid': '{test_uuid}'}}", - "test-event-1") - expected_final_data = { - 'id': 'test-id', 'subject': 'test-subject', 'dataVersion': '1.0', - 'eventType': 'test-event-1', - 'data': {'test_uuid': test_uuid} - } - - r = self.webhost.request('GET', 'eventgrid_output_binding', - params={'test_uuid': test_uuid}) - self.assertEqual(r.status_code, 200) - response = r.text - - self.assertEqual(expected_response, response) - - max_retries = 10 - for try_no in range(max_retries): - # Allow trigger to fire. - time.sleep(2) - - try: - # Check that the trigger has fired. - r = self.webhost.request('GET', - 'eventgrid_output_binding_success') - self.assertEqual(r.status_code, 200) - response = r.json() - - # list of fields to check are limited as other fields contain - # datetime or other uncertain values - for f in ['data', 'id', 'eventType', 'subject', 'dataVersion']: - self.assertEqual(response[f], expected_final_data[f]) - - except AssertionError: - if try_no == max_retries - 1: - raise - else: - break - - -class TestEventGridFunctionsStein(TestEventGridFunctions): - - @classmethod - def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'eventgrid_functions' / \ - 'eventgrid_functions_stein' - - -class TestEventGridFunctionsGeneric(TestEventGridFunctions): - - @classmethod - def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'eventgrid_functions' / \ - 'eventgrid_functions_stein' / 'generic'