diff --git a/CODEOWNERS b/CODEOWNERS index f9350110..e5f28aee 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -10,4 +10,4 @@ # For all file changes, github would automatically # include the following people in the PRs. -* @vrdmr @gavin-aguiar @YunchuWang @pdthummar @hallvictoria +* @vrdmr @gavin-aguiar @hallvictoria diff --git a/eng/ci/official-build.yml b/eng/ci/official-build.yml index ab2dc802..630545e7 100644 --- a/eng/ci/official-build.yml +++ b/eng/ci/official-build.yml @@ -52,6 +52,10 @@ extends: dependsOn: Build jobs: - template: /eng/templates/official/jobs/ci-e2e-tests.yml@self + - stage: RunEmulatorTests + dependsOn: Build + jobs: + - template: /eng/templates/official/jobs/ci-emulator-tests.yml@self - stage: RunUnitTests dependsOn: Build jobs: diff --git a/eng/ci/public-build.yml b/eng/ci/public-build.yml index 67559744..e35516da 100644 --- a/eng/ci/public-build.yml +++ b/eng/ci/public-build.yml @@ -50,4 +50,8 @@ extends: - stage: RunUnitTests dependsOn: Build jobs: - - template: /eng/templates/jobs/ci-unit-tests.yml@self \ No newline at end of file + - template: /eng/templates/jobs/ci-unit-tests.yml@self + - stage: RunEmulatorTests + dependsOn: Build + jobs: + - template: /eng/templates/jobs/ci-emulator-tests.yml@self \ No newline at end of file diff --git a/eng/templates/jobs/ci-emulator-tests.yml b/eng/templates/jobs/ci-emulator-tests.yml new file mode 100644 index 00000000..f4ebd63f --- /dev/null +++ b/eng/templates/jobs/ci-emulator-tests.yml @@ -0,0 +1,85 @@ +jobs: + - job: "TestPython" + displayName: "Run Python Emulator Tests" + + pool: + name: 1es-pool-azfunc-public + image: 1es-ubuntu-22.04 + os: linux + + strategy: + matrix: + Python37: + PYTHON_VERSION: '3.7' + Python38: + PYTHON_VERSION: '3.8' + Python39: + PYTHON_VERSION: '3.9' + Python310: + PYTHON_VERSION: '3.10' + Python311: + PYTHON_VERSION: '3.11' + Python312: + PYTHON_VERSION: '3.12' + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: $(PYTHON_VERSION) + - task: UseDotNet@2 + displayName: 'Install .NET 8' + inputs: + version: 8.0.x + - bash: | + chmod +x eng/scripts/install-dependencies.sh + chmod +x eng/scripts/test-setup.sh + + eng/scripts/install-dependencies.sh $(PYTHON_VERSION) + eng/scripts/test-setup.sh + displayName: 'Install dependencies and the worker' + condition: and(eq(variables.isSdkRelease, false), eq(variables.isExtensionsRelease, false), eq(variables['USETESTPYTHONSDK'], false), eq(variables['USETESTPYTHONEXTENSIONS'], false)) + - task: DownloadPipelineArtifact@2 + displayName: 'Download Python SDK Artifact' + inputs: + buildType: specific + artifactName: 'azure-functions' + project: 'internal' + definition: 679 + buildVersionToDownload: latest + targetPath: '$(Pipeline.Workspace)/PythonSdkArtifact' + condition: or(eq(variables.isSdkRelease, true), eq(variables['USETESTPYTHONSDK'], true)) + - bash: | + chmod +x eng/scripts/test-sdk.sh + chmod +x eng/scripts/test-setup.sh + + eng/scripts/test-sdk.sh $(Pipeline.Workspace) $(PYTHON_VERSION) + eng/scripts/test-setup.sh + displayName: 'Install test python sdk, dependencies and the worker' + condition: or(eq(variables.isSdkRelease, true), eq(variables['USETESTPYTHONSDK'], true)) + - task: DownloadPipelineArtifact@2 + displayName: 'Download Python Extension Artifact' + inputs: + buildType: specific + artifactName: $(PYTHONEXTENSIONNAME) + project: 'internal' + definition: 798 + buildVersionToDownload: latest + targetPath: '$(Pipeline.Workspace)/PythonExtensionArtifact' + condition: or(eq(variables.isExtensionsRelease, true), eq(variables['USETESTPYTHONEXTENSIONS'], true)) + - bash: | + chmod +x eng/scripts/test-setup.sh + chmod +x eng/scripts/test-extensions.sh + + eng/scripts/test-extensions.sh $(Pipeline.Workspace) $(PYTHON_VERSION) + eng/scripts/test-setup.sh + displayName: 'Install test python extension, dependencies and the worker' + condition: or(eq(variables.isExtensionsRelease, true), eq(variables['USETESTPYTHONEXTENSIONS'], true)) + - bash: | + docker compose -f tests/emulator_tests/utils/docker-compose.yml pull + docker compose -f tests/emulator_tests/utils/docker-compose.yml up -d + displayName: 'Install Azurite and Start Emulators' + - bash: | + python -m pytest -q -n auto --dist loadfile --reruns 4 tests/emulator_tests + env: + AzureWebJobsStorage: "UseDevelopmentStorage=true" + AzureWebJobsEventHubConnectionString: "Endpoint=sb://localhost;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;" + displayName: "Running $(PYTHON_VERSION) Python Emulator Tests" diff --git a/eng/templates/official/jobs/ci-emulator-tests.yml b/eng/templates/official/jobs/ci-emulator-tests.yml new file mode 100644 index 00000000..f9683af6 --- /dev/null +++ b/eng/templates/official/jobs/ci-emulator-tests.yml @@ -0,0 +1,85 @@ +jobs: + - job: "TestPython" + displayName: "Run Python Emulator Tests" + + pool: + name: 1es-pool-azfunc + image: 1es-ubuntu-22.04 + os: linux + + strategy: + matrix: + Python37: + PYTHON_VERSION: '3.7' + Python38: + PYTHON_VERSION: '3.8' + Python39: + PYTHON_VERSION: '3.9' + Python310: + PYTHON_VERSION: '3.10' + Python311: + PYTHON_VERSION: '3.11' + Python312: + PYTHON_VERSION: '3.12' + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: $(PYTHON_VERSION) + - task: UseDotNet@2 + displayName: 'Install .NET 8' + inputs: + version: 8.0.x + - bash: | + chmod +x eng/scripts/install-dependencies.sh + chmod +x eng/scripts/test-setup.sh + + eng/scripts/install-dependencies.sh $(PYTHON_VERSION) + eng/scripts/test-setup.sh + displayName: 'Install dependencies and the worker' + condition: and(eq(variables.isSdkRelease, false), eq(variables.isExtensionsRelease, false), eq(variables['USETESTPYTHONSDK'], false), eq(variables['USETESTPYTHONEXTENSIONS'], false)) + - task: DownloadPipelineArtifact@2 + displayName: 'Download Python SDK Artifact' + inputs: + buildType: specific + artifactName: 'azure-functions' + project: 'internal' + definition: 679 + buildVersionToDownload: latest + targetPath: '$(Pipeline.Workspace)/PythonSdkArtifact' + condition: or(eq(variables.isSdkRelease, true), eq(variables['USETESTPYTHONSDK'], true)) + - bash: | + chmod +x eng/scripts/test-sdk.sh + chmod +x eng/scripts/test-setup.sh + + eng/scripts/test-sdk.sh $(Pipeline.Workspace) $(PYTHON_VERSION) + eng/scripts/test-setup.sh + displayName: 'Install test python sdk, dependencies and the worker' + condition: or(eq(variables.isSdkRelease, true), eq(variables['USETESTPYTHONSDK'], true)) + - task: DownloadPipelineArtifact@2 + displayName: 'Download Python Extension Artifact' + inputs: + buildType: specific + artifactName: $(PYTHONEXTENSIONNAME) + project: 'internal' + definition: 798 + buildVersionToDownload: latest + targetPath: '$(Pipeline.Workspace)/PythonExtensionArtifact' + condition: or(eq(variables.isExtensionsRelease, true), eq(variables['USETESTPYTHONEXTENSIONS'], true)) + - bash: | + chmod +x eng/scripts/test-setup.sh + chmod +x eng/scripts/test-extensions.sh + + eng/scripts/test-extensions.sh $(Pipeline.Workspace) $(PYTHON_VERSION) + eng/scripts/test-setup.sh + displayName: 'Install test python extension, dependencies and the worker' + condition: or(eq(variables.isExtensionsRelease, true), eq(variables['USETESTPYTHONEXTENSIONS'], true)) + - bash: | + docker compose -f tests/emulator_tests/utils/docker-compose.yml pull + docker compose -f tests/emulator_tests/utils/docker-compose.yml up -d + displayName: 'Install Azurite and Start Emulators' + - bash: | + python -m pytest -q -n auto --dist loadfile --reruns 4 tests/emulator_tests + env: + AzureWebJobsStorage: "UseDevelopmentStorage=true" + AzureWebJobsEventHubConnectionString: "Endpoint=sb://localhost;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;" + displayName: "Running $(PYTHON_VERSION) Python Emulator Tests" diff --git a/tests/endtoend/blob_functions/blob_functions_stein/function_app.py b/tests/emulator_tests/blob_functions/blob_functions_stein/function_app.py similarity index 97% rename from tests/endtoend/blob_functions/blob_functions_stein/function_app.py rename to tests/emulator_tests/blob_functions/blob_functions_stein/function_app.py index 2621a336..51349a63 100644 --- a/tests/endtoend/blob_functions/blob_functions_stein/function_app.py +++ b/tests/emulator_tests/blob_functions/blob_functions_stein/function_app.py @@ -1,393 +1,393 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. -import hashlib -import io -import json -import random -import string - -import azure.functions as func - -app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) - - -@app.function_name(name="blob_trigger") -@app.blob_trigger(arg_name="file", - path="python-worker-tests/test-blob-trigger.txt", - connection="AzureWebJobsStorage") -@app.blob_output(arg_name="$return", - path="python-worker-tests/test-blob-triggered.txt", - connection="AzureWebJobsStorage") -def blob_trigger(file: func.InputStream) -> str: - return json.dumps({ - 'name': file.name, - 'length': file.length, - 'content': file.read().decode('utf-8') - }) - - -@app.function_name(name="get_blob_as_bytes") -@app.route(route="get_blob_as_bytes") -@app.blob_input(arg_name="file", - path="python-worker-tests/test-bytes.txt", - data_type="BINARY", - connection="AzureWebJobsStorage") -def get_blob_as_bytes(req: func.HttpRequest, file: bytes) -> str: - assert isinstance(file, bytes) - return file.decode('utf-8') - - -@app.function_name(name="get_blob_as_bytes_return_http_response") -@app.route(route="get_blob_as_bytes_return_http_response") -@app.blob_input(arg_name="file", - path="python-worker-tests/shmem-test-bytes.txt", - data_type="BINARY", - connection="AzureWebJobsStorage") -def get_blob_as_bytes_return_http_response(req: func.HttpRequest, file: bytes) \ - -> func.HttpResponse: - """ - Read a blob (bytes) and respond back (in HTTP response) with the number of - bytes read and the MD5 digest of the content. - """ - assert isinstance(file, bytes) - - content_size = len(file) - content_md5 = hashlib.md5(file).hexdigest() - - response_dict = { - 'content_size': content_size, - 'content_md5': content_md5 - } - - response_body = json.dumps(response_dict, indent=2) - - return func.HttpResponse( - body=response_body, - mimetype="application/json", - status_code=200 - ) - - -@app.function_name(name="get_blob_as_bytes_stream_return_http_response") -@app.route(route="get_blob_as_bytes_stream_return_http_response") -@app.blob_input(arg_name="file", - path="python-worker-tests/shmem-test-bytes.txt", - data_type="BINARY", - connection="AzureWebJobsStorage") -def get_blob_as_bytes_stream_return_http_response(req: func.HttpRequest, - file: func.InputStream) \ - -> func.HttpResponse: - """ - Read a blob (as azf.InputStream) and respond back (in HTTP response) with - the number of bytes read and the MD5 digest of the content. - """ - file_bytes = file.read() - - content_size = len(file_bytes) - content_md5 = hashlib.md5(file_bytes).hexdigest() - - response_dict = { - 'content_size': content_size, - 'content_md5': content_md5 - } - - response_body = json.dumps(response_dict, indent=2) - - return func.HttpResponse( - body=response_body, - mimetype="application/json", - status_code=200 - ) - - -@app.function_name(name="get_blob_as_str") -@app.route(route="get_blob_as_str") -@app.blob_input(arg_name="file", - path="python-worker-tests/test-str.txt", - data_type="STRING", - connection="AzureWebJobsStorage") -def get_blob_as_str(req: func.HttpRequest, file: str) -> str: - assert isinstance(file, str) - return file - - -@app.function_name(name="get_blob_as_str_return_http_response") -@app.route(route="get_blob_as_str_return_http_response") -@app.blob_input(arg_name="file", - path="python-worker-tests/shmem-test-bytes.txt", - data_type="STRING", - connection="AzureWebJobsStorage") -def get_blob_as_str_return_http_response(req: func.HttpRequest, - file: str) -> func.HttpResponse: - """ - Read a blob (string) and respond back (in HTTP response) with the number of - characters read and the MD5 digest of the utf-8 encoded content. - """ - assert isinstance(file, str) - - num_chars = len(file) - content_bytes = file.encode('utf-8') - content_md5 = hashlib.md5(content_bytes).hexdigest() - - response_dict = { - 'num_chars': num_chars, - 'content_md5': content_md5 - } - - response_body = json.dumps(response_dict, indent=2) - - return func.HttpResponse( - body=response_body, - mimetype="application/json", - status_code=200 - ) - - -@app.function_name(name="get_blob_bytes") -@app.route(route="get_blob_bytes") -@app.blob_input(arg_name="file", - path="python-worker-tests/test-bytes.txt", - connection="AzureWebJobsStorage") -def get_blob_bytes(req: func.HttpRequest, file: func.InputStream) -> str: - return file.read().decode('utf-8') - - -@app.function_name(name="get_blob_filelike") -@app.route(route="get_blob_filelike") -@app.blob_input(arg_name="file", - path="python-worker-tests/test-filelike.txt", - connection="AzureWebJobsStorage") -def get_blob_filelike(req: func.HttpRequest, file: func.InputStream) -> str: - return file.read().decode('utf-8') - - -@app.function_name(name="get_blob_return") -@app.route(route="get_blob_return") -@app.blob_input(arg_name="file", - path="python-worker-tests/test-return.txt", - connection="AzureWebJobsStorage") -def get_blob_return(req: func.HttpRequest, file: func.InputStream) -> str: - return file.read().decode('utf-8') - - -@app.function_name(name="get_blob_str") -@app.route(route="get_blob_str") -@app.blob_input(arg_name="file", - path="python-worker-tests/test-str.txt", - connection="AzureWebJobsStorage") -def get_blob_str(req: func.HttpRequest, file: func.InputStream) -> str: - return file.read().decode('utf-8') - - -@app.function_name(name="get_blob_triggered") -@app.blob_input(arg_name="file", - path="python-worker-tests/test-blob-triggered.txt", - connection="AzureWebJobsStorage") -@app.route(route="get_blob_triggered") -def get_blob_triggered(req: func.HttpRequest, file: func.InputStream) -> str: - return file.read().decode('utf-8') - - -@app.function_name(name="put_blob_as_bytes_return_http_response") -@app.blob_output(arg_name="file", - path="python-worker-tests/shmem-test-bytes-out.txt", - data_type="BINARY", - connection="AzureWebJobsStorage") -@app.route(route="put_blob_as_bytes_return_http_response") -def put_blob_as_bytes_return_http_response(req: func.HttpRequest, - file: func.Out[ - bytes]) -> func.HttpResponse: - """ - Write a blob (bytes) and respond back (in HTTP response) with the number of - bytes written and the MD5 digest of the content. - The number of bytes to write are specified in the input HTTP request. - """ - content_size = int(req.params['content_size']) - - # When this is set, then 0x01 byte is repeated content_size number of - # times to use as input. - # This is to avoid generating random input for large size which can be - # slow. - if 'no_random_input' in req.params: - content = b'\x01' * content_size - else: - content = bytearray(random.getrandbits(8) for _ in range(content_size)) - content_md5 = hashlib.md5(content).hexdigest() - - file.set(content) - - response_dict = { - 'content_size': content_size, - 'content_md5': content_md5 - } - - response_body = json.dumps(response_dict, indent=2) - - return func.HttpResponse( - body=response_body, - mimetype="application/json", - status_code=200 - ) - - -@app.function_name(name="put_blob_as_str_return_http_response") -@app.blob_output(arg_name="file", - path="python-worker-tests/shmem-test-str-out.txt", - data_type="STRING", - connection="AzureWebJobsStorage") -@app.route(route="put_blob_as_str_return_http_response") -def put_blob_as_str_return_http_response(req: func.HttpRequest, file: func.Out[ - str]) -> func.HttpResponse: - """ - Write a blob (string) and respond back (in HTTP response) with the number of - characters written and the MD5 digest of the utf-8 encoded content. - The number of characters to write are specified in the input HTTP request. - """ - num_chars = int(req.params['num_chars']) - - content = ''.join(random.choices(string.ascii_uppercase + string.digits, - k=num_chars)) - content_bytes = content.encode('utf-8') - content_size = len(content_bytes) - content_md5 = hashlib.md5(content_bytes).hexdigest() - - file.set(content) - - response_dict = { - 'num_chars': num_chars, - 'content_size': content_size, - 'content_md5': content_md5 - } - - response_body = json.dumps(response_dict, indent=2) - - return func.HttpResponse( - body=response_body, - mimetype="application/json", - status_code=200 - ) - - -@app.function_name(name="put_blob_bytes") -@app.blob_output(arg_name="file", - path="python-worker-tests/test-bytes.txt", - connection="AzureWebJobsStorage") -@app.route(route="put_blob_bytes") -def put_blob_bytes(req: func.HttpRequest, file: func.Out[bytes]) -> str: - file.set(req.get_body()) - return 'OK' - - -@app.function_name(name="put_blob_filelike") -@app.blob_output(arg_name="file", - path="python-worker-tests/test-filelike.txt", - connection="AzureWebJobsStorage") -@app.route(route="put_blob_filelike") -def put_blob_filelike(req: func.HttpRequest, - file: func.Out[io.StringIO]) -> str: - file.set(io.StringIO('filelike')) - return 'OK' - - -@app.function_name(name="put_blob_return") -@app.blob_output(arg_name="$return", - path="python-worker-tests/test-return.txt", - connection="AzureWebJobsStorage") -@app.route(route="put_blob_return", binding_arg_name="resp") -def put_blob_return(req: func.HttpRequest, - resp: func.Out[func.HttpResponse]) -> str: - return 'FROM RETURN' - - -@app.function_name(name="put_blob_str") -@app.blob_output(arg_name="file", - path="python-worker-tests/test-str.txt", - connection="AzureWebJobsStorage") -@app.route(route="put_blob_str") -def put_blob_str(req: func.HttpRequest, file: func.Out[str]) -> str: - file.set(req.get_body()) - return 'OK' - - -@app.function_name(name="put_blob_trigger") -@app.blob_output(arg_name="file", - path="python-worker-tests/test-blob-trigger.txt", - connection="AzureWebJobsStorage") -@app.route(route="put_blob_trigger") -def put_blob_trigger(req: func.HttpRequest, file: func.Out[str]) -> str: - file.set(req.get_body()) - return 'OK' - - -def _generate_content_and_digest(content_size): - content = bytearray(random.getrandbits(8) for _ in range(content_size)) - content_md5 = hashlib.md5(content).hexdigest() - return content, content_md5 - - -@app.function_name(name="put_get_multiple_blobs_as_bytes_return_http_response") -@app.blob_input(arg_name="inputfile1", - data_type="BINARY", - path="python-worker-tests/shmem-test-bytes-1.txt", - connection="AzureWebJobsStorage") -@app.blob_input(arg_name="inputfile2", - data_type="BINARY", - path="python-worker-tests/shmem-test-bytes-2.txt", - connection="AzureWebJobsStorage") -@app.blob_output(arg_name="outputfile1", - path="python-worker-tests/shmem-test-bytes-out-1.txt", - data_type="BINARY", - connection="AzureWebJobsStorage") -@app.blob_output(arg_name="outputfile2", - path="python-worker-tests/shmem-test-bytes-out-2.txt", - data_type="BINARY", - connection="AzureWebJobsStorage") -@app.route(route="put_get_multiple_blobs_as_bytes_return_http_response") -def put_get_multiple_blobs_as_bytes_return_http_response( - req: func.HttpRequest, - inputfile1: bytes, - inputfile2: bytes, - outputfile1: func.Out[bytes], - outputfile2: func.Out[bytes]) -> func.HttpResponse: - """ - Read two blobs (bytes) and respond back (in HTTP response) with the number - of bytes read from each blob and the MD5 digest of the content of each. - Write two blobs (bytes) and respond back (in HTTP response) with the number - bytes written in each blob and the MD5 digest of the content of each. - The number of bytes to write are specified in the input HTTP request. - """ - input_content_size_1 = len(inputfile1) - input_content_size_2 = len(inputfile2) - - input_content_md5_1 = hashlib.md5(inputfile1).hexdigest() - input_content_md5_2 = hashlib.md5(inputfile2).hexdigest() - - output_content_size_1 = int(req.params['output_content_size_1']) - output_content_size_2 = int(req.params['output_content_size_2']) - - output_content_1, output_content_md5_1 = \ - _generate_content_and_digest(output_content_size_1) - output_content_2, output_content_md5_2 = \ - _generate_content_and_digest(output_content_size_2) - - outputfile1.set(output_content_1) - outputfile2.set(output_content_2) - - response_dict = { - 'input_content_size_1': input_content_size_1, - 'input_content_size_2': input_content_size_2, - 'input_content_md5_1': input_content_md5_1, - 'input_content_md5_2': input_content_md5_2, - 'output_content_size_1': output_content_size_1, - 'output_content_size_2': output_content_size_2, - 'output_content_md5_1': output_content_md5_1, - 'output_content_md5_2': output_content_md5_2 - } - - response_body = json.dumps(response_dict, indent=2) - - return func.HttpResponse( - body=response_body, - mimetype="application/json", - status_code=200 - ) +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import hashlib +import io +import json +import random +import string + +import azure.functions as func + +app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) + + +@app.function_name(name="blob_trigger") +@app.blob_trigger(arg_name="file", + path="python-worker-tests/test-blob-trigger.txt", + connection="AzureWebJobsStorage") +@app.blob_output(arg_name="$return", + path="python-worker-tests/test-blob-triggered.txt", + connection="AzureWebJobsStorage") +def blob_trigger(file: func.InputStream) -> str: + return json.dumps({ + 'name': file.name, + 'length': file.length, + 'content': file.read().decode('utf-8') + }) + + +@app.function_name(name="get_blob_as_bytes") +@app.route(route="get_blob_as_bytes") +@app.blob_input(arg_name="file", + path="python-worker-tests/test-bytes.txt", + data_type="BINARY", + connection="AzureWebJobsStorage") +def get_blob_as_bytes(req: func.HttpRequest, file: bytes) -> str: + assert isinstance(file, bytes) + return file.decode('utf-8') + + +@app.function_name(name="get_blob_as_bytes_return_http_response") +@app.route(route="get_blob_as_bytes_return_http_response") +@app.blob_input(arg_name="file", + path="python-worker-tests/shmem-test-bytes.txt", + data_type="BINARY", + connection="AzureWebJobsStorage") +def get_blob_as_bytes_return_http_response(req: func.HttpRequest, file: bytes) \ + -> func.HttpResponse: + """ + Read a blob (bytes) and respond back (in HTTP response) with the number of + bytes read and the MD5 digest of the content. + """ + assert isinstance(file, bytes) + + content_size = len(file) + content_md5 = hashlib.md5(file).hexdigest() + + response_dict = { + 'content_size': content_size, + 'content_md5': content_md5 + } + + response_body = json.dumps(response_dict, indent=2) + + return func.HttpResponse( + body=response_body, + mimetype="application/json", + status_code=200 + ) + + +@app.function_name(name="get_blob_as_bytes_stream_return_http_response") +@app.route(route="get_blob_as_bytes_stream_return_http_response") +@app.blob_input(arg_name="file", + path="python-worker-tests/shmem-test-bytes.txt", + data_type="BINARY", + connection="AzureWebJobsStorage") +def get_blob_as_bytes_stream_return_http_response(req: func.HttpRequest, + file: func.InputStream) \ + -> func.HttpResponse: + """ + Read a blob (as azf.InputStream) and respond back (in HTTP response) with + the number of bytes read and the MD5 digest of the content. + """ + file_bytes = file.read() + + content_size = len(file_bytes) + content_md5 = hashlib.md5(file_bytes).hexdigest() + + response_dict = { + 'content_size': content_size, + 'content_md5': content_md5 + } + + response_body = json.dumps(response_dict, indent=2) + + return func.HttpResponse( + body=response_body, + mimetype="application/json", + status_code=200 + ) + + +@app.function_name(name="get_blob_as_str") +@app.route(route="get_blob_as_str") +@app.blob_input(arg_name="file", + path="python-worker-tests/test-str.txt", + data_type="STRING", + connection="AzureWebJobsStorage") +def get_blob_as_str(req: func.HttpRequest, file: str) -> str: + assert isinstance(file, str) + return file + + +@app.function_name(name="get_blob_as_str_return_http_response") +@app.route(route="get_blob_as_str_return_http_response") +@app.blob_input(arg_name="file", + path="python-worker-tests/shmem-test-bytes.txt", + data_type="STRING", + connection="AzureWebJobsStorage") +def get_blob_as_str_return_http_response(req: func.HttpRequest, + file: str) -> func.HttpResponse: + """ + Read a blob (string) and respond back (in HTTP response) with the number of + characters read and the MD5 digest of the utf-8 encoded content. + """ + assert isinstance(file, str) + + num_chars = len(file) + content_bytes = file.encode('utf-8') + content_md5 = hashlib.md5(content_bytes).hexdigest() + + response_dict = { + 'num_chars': num_chars, + 'content_md5': content_md5 + } + + response_body = json.dumps(response_dict, indent=2) + + return func.HttpResponse( + body=response_body, + mimetype="application/json", + status_code=200 + ) + + +@app.function_name(name="get_blob_bytes") +@app.route(route="get_blob_bytes") +@app.blob_input(arg_name="file", + path="python-worker-tests/test-bytes.txt", + connection="AzureWebJobsStorage") +def get_blob_bytes(req: func.HttpRequest, file: func.InputStream) -> str: + return file.read().decode('utf-8') + + +@app.function_name(name="get_blob_filelike") +@app.route(route="get_blob_filelike") +@app.blob_input(arg_name="file", + path="python-worker-tests/test-filelike.txt", + connection="AzureWebJobsStorage") +def get_blob_filelike(req: func.HttpRequest, file: func.InputStream) -> str: + return file.read().decode('utf-8') + + +@app.function_name(name="get_blob_return") +@app.route(route="get_blob_return") +@app.blob_input(arg_name="file", + path="python-worker-tests/test-return.txt", + connection="AzureWebJobsStorage") +def get_blob_return(req: func.HttpRequest, file: func.InputStream) -> str: + return file.read().decode('utf-8') + + +@app.function_name(name="get_blob_str") +@app.route(route="get_blob_str") +@app.blob_input(arg_name="file", + path="python-worker-tests/test-str.txt", + connection="AzureWebJobsStorage") +def get_blob_str(req: func.HttpRequest, file: func.InputStream) -> str: + return file.read().decode('utf-8') + + +@app.function_name(name="get_blob_triggered") +@app.blob_input(arg_name="file", + path="python-worker-tests/test-blob-triggered.txt", + connection="AzureWebJobsStorage") +@app.route(route="get_blob_triggered") +def get_blob_triggered(req: func.HttpRequest, file: func.InputStream) -> str: + return file.read().decode('utf-8') + + +@app.function_name(name="put_blob_as_bytes_return_http_response") +@app.blob_output(arg_name="file", + path="python-worker-tests/shmem-test-bytes-out.txt", + data_type="BINARY", + connection="AzureWebJobsStorage") +@app.route(route="put_blob_as_bytes_return_http_response") +def put_blob_as_bytes_return_http_response(req: func.HttpRequest, + file: func.Out[ + bytes]) -> func.HttpResponse: + """ + Write a blob (bytes) and respond back (in HTTP response) with the number of + bytes written and the MD5 digest of the content. + The number of bytes to write are specified in the input HTTP request. + """ + content_size = int(req.params['content_size']) + + # When this is set, then 0x01 byte is repeated content_size number of + # times to use as input. + # This is to avoid generating random input for large size which can be + # slow. + if 'no_random_input' in req.params: + content = b'\x01' * content_size + else: + content = bytearray(random.getrandbits(8) for _ in range(content_size)) + content_md5 = hashlib.md5(content).hexdigest() + + file.set(content) + + response_dict = { + 'content_size': content_size, + 'content_md5': content_md5 + } + + response_body = json.dumps(response_dict, indent=2) + + return func.HttpResponse( + body=response_body, + mimetype="application/json", + status_code=200 + ) + + +@app.function_name(name="put_blob_as_str_return_http_response") +@app.blob_output(arg_name="file", + path="python-worker-tests/shmem-test-str-out.txt", + data_type="STRING", + connection="AzureWebJobsStorage") +@app.route(route="put_blob_as_str_return_http_response") +def put_blob_as_str_return_http_response(req: func.HttpRequest, file: func.Out[ + str]) -> func.HttpResponse: + """ + Write a blob (string) and respond back (in HTTP response) with the number of + characters written and the MD5 digest of the utf-8 encoded content. + The number of characters to write are specified in the input HTTP request. + """ + num_chars = int(req.params['num_chars']) + + content = ''.join(random.choices(string.ascii_uppercase + string.digits, + k=num_chars)) + content_bytes = content.encode('utf-8') + content_size = len(content_bytes) + content_md5 = hashlib.md5(content_bytes).hexdigest() + + file.set(content) + + response_dict = { + 'num_chars': num_chars, + 'content_size': content_size, + 'content_md5': content_md5 + } + + response_body = json.dumps(response_dict, indent=2) + + return func.HttpResponse( + body=response_body, + mimetype="application/json", + status_code=200 + ) + + +@app.function_name(name="put_blob_bytes") +@app.blob_output(arg_name="file", + path="python-worker-tests/test-bytes.txt", + connection="AzureWebJobsStorage") +@app.route(route="put_blob_bytes") +def put_blob_bytes(req: func.HttpRequest, file: func.Out[bytes]) -> str: + file.set(req.get_body()) + return 'OK' + + +@app.function_name(name="put_blob_filelike") +@app.blob_output(arg_name="file", + path="python-worker-tests/test-filelike.txt", + connection="AzureWebJobsStorage") +@app.route(route="put_blob_filelike") +def put_blob_filelike(req: func.HttpRequest, + file: func.Out[io.StringIO]) -> str: + file.set(io.StringIO('filelike')) + return 'OK' + + +@app.function_name(name="put_blob_return") +@app.blob_output(arg_name="$return", + path="python-worker-tests/test-return.txt", + connection="AzureWebJobsStorage") +@app.route(route="put_blob_return", binding_arg_name="resp") +def put_blob_return(req: func.HttpRequest, + resp: func.Out[func.HttpResponse]) -> str: + return 'FROM RETURN' + + +@app.function_name(name="put_blob_str") +@app.blob_output(arg_name="file", + path="python-worker-tests/test-str.txt", + connection="AzureWebJobsStorage") +@app.route(route="put_blob_str") +def put_blob_str(req: func.HttpRequest, file: func.Out[str]) -> str: + file.set(req.get_body()) + return 'OK' + + +@app.function_name(name="put_blob_trigger") +@app.blob_output(arg_name="file", + path="python-worker-tests/test-blob-trigger.txt", + connection="AzureWebJobsStorage") +@app.route(route="put_blob_trigger") +def put_blob_trigger(req: func.HttpRequest, file: func.Out[str]) -> str: + file.set(req.get_body()) + return 'OK' + + +def _generate_content_and_digest(content_size): + content = bytearray(random.getrandbits(8) for _ in range(content_size)) + content_md5 = hashlib.md5(content).hexdigest() + return content, content_md5 + + +@app.function_name(name="put_get_multiple_blobs_as_bytes_return_http_response") +@app.blob_input(arg_name="inputfile1", + data_type="BINARY", + path="python-worker-tests/shmem-test-bytes-1.txt", + connection="AzureWebJobsStorage") +@app.blob_input(arg_name="inputfile2", + data_type="BINARY", + path="python-worker-tests/shmem-test-bytes-2.txt", + connection="AzureWebJobsStorage") +@app.blob_output(arg_name="outputfile1", + path="python-worker-tests/shmem-test-bytes-out-1.txt", + data_type="BINARY", + connection="AzureWebJobsStorage") +@app.blob_output(arg_name="outputfile2", + path="python-worker-tests/shmem-test-bytes-out-2.txt", + data_type="BINARY", + connection="AzureWebJobsStorage") +@app.route(route="put_get_multiple_blobs_as_bytes_return_http_response") +def put_get_multiple_blobs_as_bytes_return_http_response( + req: func.HttpRequest, + inputfile1: bytes, + inputfile2: bytes, + outputfile1: func.Out[bytes], + outputfile2: func.Out[bytes]) -> func.HttpResponse: + """ + Read two blobs (bytes) and respond back (in HTTP response) with the number + of bytes read from each blob and the MD5 digest of the content of each. + Write two blobs (bytes) and respond back (in HTTP response) with the number + bytes written in each blob and the MD5 digest of the content of each. + The number of bytes to write are specified in the input HTTP request. + """ + input_content_size_1 = len(inputfile1) + input_content_size_2 = len(inputfile2) + + input_content_md5_1 = hashlib.md5(inputfile1).hexdigest() + input_content_md5_2 = hashlib.md5(inputfile2).hexdigest() + + output_content_size_1 = int(req.params['output_content_size_1']) + output_content_size_2 = int(req.params['output_content_size_2']) + + output_content_1, output_content_md5_1 = \ + _generate_content_and_digest(output_content_size_1) + output_content_2, output_content_md5_2 = \ + _generate_content_and_digest(output_content_size_2) + + outputfile1.set(output_content_1) + outputfile2.set(output_content_2) + + response_dict = { + 'input_content_size_1': input_content_size_1, + 'input_content_size_2': input_content_size_2, + 'input_content_md5_1': input_content_md5_1, + 'input_content_md5_2': input_content_md5_2, + 'output_content_size_1': output_content_size_1, + 'output_content_size_2': output_content_size_2, + 'output_content_md5_1': output_content_md5_1, + 'output_content_md5_2': output_content_md5_2 + } + + response_body = json.dumps(response_dict, indent=2) + + return func.HttpResponse( + body=response_body, + mimetype="application/json", + status_code=200 + ) diff --git a/tests/endtoend/blob_functions/blob_functions_stein/generic/function_app.py b/tests/emulator_tests/blob_functions/blob_functions_stein/generic/function_app.py similarity index 100% rename from tests/endtoend/blob_functions/blob_functions_stein/generic/function_app.py rename to tests/emulator_tests/blob_functions/blob_functions_stein/generic/function_app.py diff --git a/tests/endtoend/blob_functions/blob_trigger/function.json b/tests/emulator_tests/blob_functions/blob_trigger/function.json similarity index 100% rename from tests/endtoend/blob_functions/blob_trigger/function.json rename to tests/emulator_tests/blob_functions/blob_trigger/function.json diff --git a/tests/endtoend/blob_functions/blob_trigger/main.py b/tests/emulator_tests/blob_functions/blob_trigger/main.py similarity index 100% rename from tests/endtoend/blob_functions/blob_trigger/main.py rename to tests/emulator_tests/blob_functions/blob_trigger/main.py diff --git a/tests/endtoend/blob_functions/get_blob_as_bytes/function.json b/tests/emulator_tests/blob_functions/get_blob_as_bytes/function.json similarity index 100% rename from tests/endtoend/blob_functions/get_blob_as_bytes/function.json rename to tests/emulator_tests/blob_functions/get_blob_as_bytes/function.json diff --git a/tests/endtoend/blob_functions/get_blob_as_bytes/main.py b/tests/emulator_tests/blob_functions/get_blob_as_bytes/main.py similarity index 100% rename from tests/endtoend/blob_functions/get_blob_as_bytes/main.py rename to tests/emulator_tests/blob_functions/get_blob_as_bytes/main.py diff --git a/tests/endtoend/blob_functions/get_blob_as_bytes_return_http_response/function.json b/tests/emulator_tests/blob_functions/get_blob_as_bytes_return_http_response/function.json similarity index 100% rename from tests/endtoend/blob_functions/get_blob_as_bytes_return_http_response/function.json rename to tests/emulator_tests/blob_functions/get_blob_as_bytes_return_http_response/function.json diff --git a/tests/endtoend/blob_functions/get_blob_as_bytes_return_http_response/main.py b/tests/emulator_tests/blob_functions/get_blob_as_bytes_return_http_response/main.py similarity index 100% rename from tests/endtoend/blob_functions/get_blob_as_bytes_return_http_response/main.py rename to tests/emulator_tests/blob_functions/get_blob_as_bytes_return_http_response/main.py diff --git a/tests/endtoend/blob_functions/get_blob_as_bytes_stream_return_http_response/function.json b/tests/emulator_tests/blob_functions/get_blob_as_bytes_stream_return_http_response/function.json similarity index 100% rename from tests/endtoend/blob_functions/get_blob_as_bytes_stream_return_http_response/function.json rename to tests/emulator_tests/blob_functions/get_blob_as_bytes_stream_return_http_response/function.json diff --git a/tests/endtoend/blob_functions/get_blob_as_bytes_stream_return_http_response/main.py b/tests/emulator_tests/blob_functions/get_blob_as_bytes_stream_return_http_response/main.py similarity index 100% rename from tests/endtoend/blob_functions/get_blob_as_bytes_stream_return_http_response/main.py rename to tests/emulator_tests/blob_functions/get_blob_as_bytes_stream_return_http_response/main.py diff --git a/tests/endtoend/blob_functions/get_blob_as_str/function.json b/tests/emulator_tests/blob_functions/get_blob_as_str/function.json similarity index 100% rename from tests/endtoend/blob_functions/get_blob_as_str/function.json rename to tests/emulator_tests/blob_functions/get_blob_as_str/function.json diff --git a/tests/endtoend/blob_functions/get_blob_as_str/main.py b/tests/emulator_tests/blob_functions/get_blob_as_str/main.py similarity index 100% rename from tests/endtoend/blob_functions/get_blob_as_str/main.py rename to tests/emulator_tests/blob_functions/get_blob_as_str/main.py diff --git a/tests/endtoend/blob_functions/get_blob_as_str_return_http_response/function.json b/tests/emulator_tests/blob_functions/get_blob_as_str_return_http_response/function.json similarity index 100% rename from tests/endtoend/blob_functions/get_blob_as_str_return_http_response/function.json rename to tests/emulator_tests/blob_functions/get_blob_as_str_return_http_response/function.json diff --git a/tests/endtoend/blob_functions/get_blob_as_str_return_http_response/main.py b/tests/emulator_tests/blob_functions/get_blob_as_str_return_http_response/main.py similarity index 100% rename from tests/endtoend/blob_functions/get_blob_as_str_return_http_response/main.py rename to tests/emulator_tests/blob_functions/get_blob_as_str_return_http_response/main.py diff --git a/tests/endtoend/blob_functions/get_blob_bytes/function.json b/tests/emulator_tests/blob_functions/get_blob_bytes/function.json similarity index 100% rename from tests/endtoend/blob_functions/get_blob_bytes/function.json rename to tests/emulator_tests/blob_functions/get_blob_bytes/function.json diff --git a/tests/endtoend/blob_functions/get_blob_bytes/main.py b/tests/emulator_tests/blob_functions/get_blob_bytes/main.py similarity index 100% rename from tests/endtoend/blob_functions/get_blob_bytes/main.py rename to tests/emulator_tests/blob_functions/get_blob_bytes/main.py diff --git a/tests/endtoend/blob_functions/get_blob_filelike/function.json b/tests/emulator_tests/blob_functions/get_blob_filelike/function.json similarity index 100% rename from tests/endtoend/blob_functions/get_blob_filelike/function.json rename to tests/emulator_tests/blob_functions/get_blob_filelike/function.json diff --git a/tests/endtoend/blob_functions/get_blob_filelike/main.py b/tests/emulator_tests/blob_functions/get_blob_filelike/main.py similarity index 100% rename from tests/endtoend/blob_functions/get_blob_filelike/main.py rename to tests/emulator_tests/blob_functions/get_blob_filelike/main.py diff --git a/tests/endtoend/blob_functions/get_blob_return/function.json b/tests/emulator_tests/blob_functions/get_blob_return/function.json similarity index 100% rename from tests/endtoend/blob_functions/get_blob_return/function.json rename to tests/emulator_tests/blob_functions/get_blob_return/function.json diff --git a/tests/endtoend/blob_functions/get_blob_return/main.py b/tests/emulator_tests/blob_functions/get_blob_return/main.py similarity index 100% rename from tests/endtoend/blob_functions/get_blob_return/main.py rename to tests/emulator_tests/blob_functions/get_blob_return/main.py diff --git a/tests/endtoend/blob_functions/get_blob_str/function.json b/tests/emulator_tests/blob_functions/get_blob_str/function.json similarity index 100% rename from tests/endtoend/blob_functions/get_blob_str/function.json rename to tests/emulator_tests/blob_functions/get_blob_str/function.json diff --git a/tests/endtoend/blob_functions/get_blob_str/main.py b/tests/emulator_tests/blob_functions/get_blob_str/main.py similarity index 100% rename from tests/endtoend/blob_functions/get_blob_str/main.py rename to tests/emulator_tests/blob_functions/get_blob_str/main.py diff --git a/tests/endtoend/blob_functions/get_blob_triggered/function.json b/tests/emulator_tests/blob_functions/get_blob_triggered/function.json similarity index 100% rename from tests/endtoend/blob_functions/get_blob_triggered/function.json rename to tests/emulator_tests/blob_functions/get_blob_triggered/function.json diff --git a/tests/endtoend/blob_functions/get_blob_triggered/main.py b/tests/emulator_tests/blob_functions/get_blob_triggered/main.py similarity index 100% rename from tests/endtoend/blob_functions/get_blob_triggered/main.py rename to tests/emulator_tests/blob_functions/get_blob_triggered/main.py diff --git a/tests/endtoend/blob_functions/put_blob_as_bytes_return_http_response/function.json b/tests/emulator_tests/blob_functions/put_blob_as_bytes_return_http_response/function.json similarity index 100% rename from tests/endtoend/blob_functions/put_blob_as_bytes_return_http_response/function.json rename to tests/emulator_tests/blob_functions/put_blob_as_bytes_return_http_response/function.json diff --git a/tests/endtoend/blob_functions/put_blob_as_bytes_return_http_response/main.py b/tests/emulator_tests/blob_functions/put_blob_as_bytes_return_http_response/main.py similarity index 100% rename from tests/endtoend/blob_functions/put_blob_as_bytes_return_http_response/main.py rename to tests/emulator_tests/blob_functions/put_blob_as_bytes_return_http_response/main.py diff --git a/tests/endtoend/blob_functions/put_blob_as_str_return_http_response/function.json b/tests/emulator_tests/blob_functions/put_blob_as_str_return_http_response/function.json similarity index 100% rename from tests/endtoend/blob_functions/put_blob_as_str_return_http_response/function.json rename to tests/emulator_tests/blob_functions/put_blob_as_str_return_http_response/function.json diff --git a/tests/endtoend/blob_functions/put_blob_as_str_return_http_response/main.py b/tests/emulator_tests/blob_functions/put_blob_as_str_return_http_response/main.py similarity index 100% rename from tests/endtoend/blob_functions/put_blob_as_str_return_http_response/main.py rename to tests/emulator_tests/blob_functions/put_blob_as_str_return_http_response/main.py diff --git a/tests/endtoend/blob_functions/put_blob_bytes/function.json b/tests/emulator_tests/blob_functions/put_blob_bytes/function.json similarity index 100% rename from tests/endtoend/blob_functions/put_blob_bytes/function.json rename to tests/emulator_tests/blob_functions/put_blob_bytes/function.json diff --git a/tests/endtoend/blob_functions/put_blob_bytes/main.py b/tests/emulator_tests/blob_functions/put_blob_bytes/main.py similarity index 100% rename from tests/endtoend/blob_functions/put_blob_bytes/main.py rename to tests/emulator_tests/blob_functions/put_blob_bytes/main.py diff --git a/tests/endtoend/blob_functions/put_blob_filelike/function.json b/tests/emulator_tests/blob_functions/put_blob_filelike/function.json similarity index 100% rename from tests/endtoend/blob_functions/put_blob_filelike/function.json rename to tests/emulator_tests/blob_functions/put_blob_filelike/function.json diff --git a/tests/endtoend/blob_functions/put_blob_filelike/main.py b/tests/emulator_tests/blob_functions/put_blob_filelike/main.py similarity index 100% rename from tests/endtoend/blob_functions/put_blob_filelike/main.py rename to tests/emulator_tests/blob_functions/put_blob_filelike/main.py diff --git a/tests/endtoend/blob_functions/put_blob_return/function.json b/tests/emulator_tests/blob_functions/put_blob_return/function.json similarity index 100% rename from tests/endtoend/blob_functions/put_blob_return/function.json rename to tests/emulator_tests/blob_functions/put_blob_return/function.json diff --git a/tests/endtoend/blob_functions/put_blob_return/main.py b/tests/emulator_tests/blob_functions/put_blob_return/main.py similarity index 100% rename from tests/endtoend/blob_functions/put_blob_return/main.py rename to tests/emulator_tests/blob_functions/put_blob_return/main.py diff --git a/tests/endtoend/blob_functions/put_blob_str/function.json b/tests/emulator_tests/blob_functions/put_blob_str/function.json similarity index 100% rename from tests/endtoend/blob_functions/put_blob_str/function.json rename to tests/emulator_tests/blob_functions/put_blob_str/function.json diff --git a/tests/endtoend/blob_functions/put_blob_str/main.py b/tests/emulator_tests/blob_functions/put_blob_str/main.py similarity index 100% rename from tests/endtoend/blob_functions/put_blob_str/main.py rename to tests/emulator_tests/blob_functions/put_blob_str/main.py diff --git a/tests/endtoend/blob_functions/put_blob_trigger/function.json b/tests/emulator_tests/blob_functions/put_blob_trigger/function.json similarity index 100% rename from tests/endtoend/blob_functions/put_blob_trigger/function.json rename to tests/emulator_tests/blob_functions/put_blob_trigger/function.json diff --git a/tests/endtoend/blob_functions/put_blob_trigger/main.py b/tests/emulator_tests/blob_functions/put_blob_trigger/main.py similarity index 100% rename from tests/endtoend/blob_functions/put_blob_trigger/main.py rename to tests/emulator_tests/blob_functions/put_blob_trigger/main.py diff --git a/tests/endtoend/blob_functions/put_get_multiple_blobs_as_bytes_return_http_response/function.json b/tests/emulator_tests/blob_functions/put_get_multiple_blobs_as_bytes_return_http_response/function.json similarity index 100% rename from tests/endtoend/blob_functions/put_get_multiple_blobs_as_bytes_return_http_response/function.json rename to tests/emulator_tests/blob_functions/put_get_multiple_blobs_as_bytes_return_http_response/function.json diff --git a/tests/endtoend/blob_functions/put_get_multiple_blobs_as_bytes_return_http_response/main.py b/tests/emulator_tests/blob_functions/put_get_multiple_blobs_as_bytes_return_http_response/main.py similarity index 100% rename from tests/endtoend/blob_functions/put_get_multiple_blobs_as_bytes_return_http_response/main.py rename to tests/emulator_tests/blob_functions/put_get_multiple_blobs_as_bytes_return_http_response/main.py diff --git a/tests/endtoend/eventhub_batch_functions/eventhub_batch_functions_stein/function_app.py b/tests/emulator_tests/eventhub_batch_functions/eventhub_batch_functions_stein/function_app.py similarity index 100% rename from tests/endtoend/eventhub_batch_functions/eventhub_batch_functions_stein/function_app.py rename to tests/emulator_tests/eventhub_batch_functions/eventhub_batch_functions_stein/function_app.py diff --git a/tests/endtoend/eventhub_batch_functions/eventhub_multiple/__init__.py b/tests/emulator_tests/eventhub_batch_functions/eventhub_multiple/__init__.py similarity index 100% rename from tests/endtoend/eventhub_batch_functions/eventhub_multiple/__init__.py rename to tests/emulator_tests/eventhub_batch_functions/eventhub_multiple/__init__.py diff --git a/tests/endtoend/eventhub_batch_functions/eventhub_multiple/function.json b/tests/emulator_tests/eventhub_batch_functions/eventhub_multiple/function.json similarity index 100% rename from tests/endtoend/eventhub_batch_functions/eventhub_multiple/function.json rename to tests/emulator_tests/eventhub_batch_functions/eventhub_multiple/function.json diff --git a/tests/endtoend/eventhub_batch_functions/eventhub_output_batch/__init__.py b/tests/emulator_tests/eventhub_batch_functions/eventhub_output_batch/__init__.py similarity index 100% rename from tests/endtoend/eventhub_batch_functions/eventhub_output_batch/__init__.py rename to tests/emulator_tests/eventhub_batch_functions/eventhub_output_batch/__init__.py diff --git a/tests/endtoend/eventhub_batch_functions/eventhub_output_batch/function.json b/tests/emulator_tests/eventhub_batch_functions/eventhub_output_batch/function.json similarity index 100% rename from tests/endtoend/eventhub_batch_functions/eventhub_output_batch/function.json rename to tests/emulator_tests/eventhub_batch_functions/eventhub_output_batch/function.json diff --git a/tests/endtoend/eventhub_batch_functions/get_eventhub_batch_triggered/__init__.py b/tests/emulator_tests/eventhub_batch_functions/get_eventhub_batch_triggered/__init__.py similarity index 100% rename from tests/endtoend/eventhub_batch_functions/get_eventhub_batch_triggered/__init__.py rename to tests/emulator_tests/eventhub_batch_functions/get_eventhub_batch_triggered/__init__.py diff --git a/tests/endtoend/eventhub_batch_functions/get_eventhub_batch_triggered/function.json b/tests/emulator_tests/eventhub_batch_functions/get_eventhub_batch_triggered/function.json similarity index 95% rename from tests/endtoend/eventhub_batch_functions/get_eventhub_batch_triggered/function.json rename to tests/emulator_tests/eventhub_batch_functions/get_eventhub_batch_triggered/function.json index 3e8a6995..8ec2e9d6 100644 --- a/tests/endtoend/eventhub_batch_functions/get_eventhub_batch_triggered/function.json +++ b/tests/emulator_tests/eventhub_batch_functions/get_eventhub_batch_triggered/function.json @@ -1,26 +1,26 @@ -{ - "scriptFile": "__init__.py", - "bindings": [ - { - "type": "httpTrigger", - "direction": "in", - "authLevel": "anonymous", - "methods": [ - "get" - ], - "name": "req" - }, - { - "direction": "in", - "type": "blob", - "name": "testEntities", - "path": "python-worker-tests/test-eventhub-batch-triggered.txt", - "connection": "AzureWebJobsStorage" - }, - { - "type": "http", - "direction": "out", - "name": "$return" - } - ] +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "type": "httpTrigger", + "direction": "in", + "authLevel": "anonymous", + "methods": [ + "get" + ], + "name": "req" + }, + { + "direction": "in", + "type": "blob", + "name": "testEntities", + "path": "python-worker-tests/test-eventhub-batch-triggered.txt", + "connection": "AzureWebJobsStorage" + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] } \ No newline at end of file diff --git a/tests/endtoend/eventhub_batch_functions/get_metadata_batch_triggered/__init__.py b/tests/emulator_tests/eventhub_batch_functions/get_metadata_batch_triggered/__init__.py similarity index 100% rename from tests/endtoend/eventhub_batch_functions/get_metadata_batch_triggered/__init__.py rename to tests/emulator_tests/eventhub_batch_functions/get_metadata_batch_triggered/__init__.py diff --git a/tests/endtoend/eventhub_batch_functions/get_metadata_batch_triggered/function.json b/tests/emulator_tests/eventhub_batch_functions/get_metadata_batch_triggered/function.json similarity index 100% rename from tests/endtoend/eventhub_batch_functions/get_metadata_batch_triggered/function.json rename to tests/emulator_tests/eventhub_batch_functions/get_metadata_batch_triggered/function.json diff --git a/tests/endtoend/eventhub_batch_functions/metadata_multiple/__init__.py b/tests/emulator_tests/eventhub_batch_functions/metadata_multiple/__init__.py similarity index 100% rename from tests/endtoend/eventhub_batch_functions/metadata_multiple/__init__.py rename to tests/emulator_tests/eventhub_batch_functions/metadata_multiple/__init__.py diff --git a/tests/endtoend/eventhub_batch_functions/metadata_multiple/function.json b/tests/emulator_tests/eventhub_batch_functions/metadata_multiple/function.json similarity index 100% rename from tests/endtoend/eventhub_batch_functions/metadata_multiple/function.json rename to tests/emulator_tests/eventhub_batch_functions/metadata_multiple/function.json diff --git a/tests/endtoend/eventhub_batch_functions/metadata_output_batch/__init__.py b/tests/emulator_tests/eventhub_batch_functions/metadata_output_batch/__init__.py similarity index 100% rename from tests/endtoend/eventhub_batch_functions/metadata_output_batch/__init__.py rename to tests/emulator_tests/eventhub_batch_functions/metadata_output_batch/__init__.py diff --git a/tests/endtoend/eventhub_batch_functions/metadata_output_batch/function.json b/tests/emulator_tests/eventhub_batch_functions/metadata_output_batch/function.json similarity index 100% rename from tests/endtoend/eventhub_batch_functions/metadata_output_batch/function.json rename to tests/emulator_tests/eventhub_batch_functions/metadata_output_batch/function.json diff --git a/tests/endtoend/eventhub_functions/eventhub_functions_stein/function_app.py b/tests/emulator_tests/eventhub_functions/eventhub_functions_stein/function_app.py similarity index 97% rename from tests/endtoend/eventhub_functions/eventhub_functions_stein/function_app.py rename to tests/emulator_tests/eventhub_functions/eventhub_functions_stein/function_app.py index 1091f8dd..1481f7b5 100644 --- a/tests/endtoend/eventhub_functions/eventhub_functions_stein/function_app.py +++ b/tests/emulator_tests/eventhub_functions/eventhub_functions_stein/function_app.py @@ -1,107 +1,107 @@ -import json -import os -import typing - -import azure.functions as func -from azure.eventhub import EventData -from azure.eventhub.aio import EventHubProducerClient - -app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) - - -# An HttpTrigger to generating EventHub event from EventHub Output Binding -@app.function_name(name="eventhub_output") -@app.route(route="eventhub_output") -@app.event_hub_output(arg_name="event", - event_hub_name="python-worker-ci-eventhub-one", - connection="AzureWebJobsEventHubConnectionString") -def eventhub_output(req: func.HttpRequest, event: func.Out[str]): - event.set(req.get_body().decode('utf-8')) - return 'OK' - - -# This is an actual EventHub trigger which will convert the event data -# into a storage blob. -@app.function_name(name="eventhub_trigger") -@app.event_hub_message_trigger(arg_name="event", - event_hub_name="python-worker-ci-eventhub-one", - connection="AzureWebJobsEventHubConnectionString" - ) -@app.blob_output(arg_name="$return", - path="python-worker-tests/test-eventhub-triggered.txt", - connection="AzureWebJobsStorage") -def eventhub_trigger(event: func.EventHubEvent) -> bytes: - return event.get_body() - - -# Retrieve the event data from storage blob and return it as Http response -@app.function_name(name="get_eventhub_triggered") -@app.route(route="get_eventhub_triggered") -@app.blob_input(arg_name="file", - path="python-worker-tests/test-eventhub-triggered.txt", - connection="AzureWebJobsStorage") -def get_eventhub_triggered(req: func.HttpRequest, - file: func.InputStream) -> str: - return file.read().decode('utf-8') - - -# Retrieve the event data from storage blob and return it as Http response -@app.function_name(name="get_metadata_triggered") -@app.route(route="get_metadata_triggered") -@app.blob_input(arg_name="file", - path="python-worker-tests/test-metadata-triggered.txt", - connection="AzureWebJobsStorage") -async def get_metadata_triggered(req: func.HttpRequest, - file: func.InputStream) -> str: - return func.HttpResponse(body=file.read().decode('utf-8'), - status_code=200, - mimetype='application/json') - - -# An HttpTrigger to generating EventHub event from azure-eventhub SDK. -# Events generated from azure-eventhub contain the full metadata. -@app.function_name(name="metadata_output") -@app.route(route="metadata_output") -async def metadata_output(req: func.HttpRequest): - # Parse event metadata from http request - json_string = req.get_body().decode('utf-8') - event_dict = json.loads(json_string) - - # Create an EventHub Client and event batch - client = EventHubProducerClient.from_connection_string( - os.getenv('AzureWebJobsEventHubConnectionString'), - eventhub_name='python-worker-ci-eventhub-one-metadata') - - # Generate new event based on http request with full metadata - event_data_batch = await client.create_batch() - event_data_batch.add(EventData(event_dict.get('body'))) - - # Send out event into event hub - try: - await client.send_batch(event_data_batch) - finally: - await client.close() - - return 'OK' - - -@app.function_name(name="metadata_trigger") -@app.event_hub_message_trigger( - arg_name="event", - event_hub_name="python-worker-ci-eventhub-one-metadata", - connection="AzureWebJobsEventHubConnectionString") -@app.blob_output(arg_name="$return", - path="python-worker-tests/test-metadata-triggered.txt", - connection="AzureWebJobsStorage") -async def metadata_trigger(event: func.EventHubEvent) -> bytes: - event_dict: typing.Mapping[str, typing.Any] = { - 'body': event.get_body().decode('utf-8'), - # Uncomment this when the EnqueuedTimeUtc is fixed in azure-functions - # 'enqueued_time': event.enqueued_time.isoformat(), - 'partition_key': event.partition_key, - 'sequence_number': event.sequence_number, - 'offset': event.offset, - 'metadata': event.metadata - } - - return json.dumps(event_dict) +import json +import os +import typing + +import azure.functions as func +from azure.eventhub import EventData +from azure.eventhub.aio import EventHubProducerClient + +app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) + + +# An HttpTrigger to generating EventHub event from EventHub Output Binding +@app.function_name(name="eventhub_output") +@app.route(route="eventhub_output") +@app.event_hub_output(arg_name="event", + event_hub_name="python-worker-ci-eventhub-one", + connection="AzureWebJobsEventHubConnectionString") +def eventhub_output(req: func.HttpRequest, event: func.Out[str]): + event.set(req.get_body().decode('utf-8')) + return 'OK' + + +# This is an actual EventHub trigger which will convert the event data +# into a storage blob. +@app.function_name(name="eventhub_trigger") +@app.event_hub_message_trigger(arg_name="event", + event_hub_name="python-worker-ci-eventhub-one", + connection="AzureWebJobsEventHubConnectionString" + ) +@app.blob_output(arg_name="$return", + path="python-worker-tests/test-eventhub-triggered.txt", + connection="AzureWebJobsStorage") +def eventhub_trigger(event: func.EventHubEvent) -> bytes: + return event.get_body() + + +# Retrieve the event data from storage blob and return it as Http response +@app.function_name(name="get_eventhub_triggered") +@app.route(route="get_eventhub_triggered") +@app.blob_input(arg_name="file", + path="python-worker-tests/test-eventhub-triggered.txt", + connection="AzureWebJobsStorage") +def get_eventhub_triggered(req: func.HttpRequest, + file: func.InputStream) -> str: + return file.read().decode('utf-8') + + +# Retrieve the event data from storage blob and return it as Http response +@app.function_name(name="get_metadata_triggered") +@app.route(route="get_metadata_triggered") +@app.blob_input(arg_name="file", + path="python-worker-tests/test-metadata-triggered.txt", + connection="AzureWebJobsStorage") +async def get_metadata_triggered(req: func.HttpRequest, + file: func.InputStream) -> str: + return func.HttpResponse(body=file.read().decode('utf-8'), + status_code=200, + mimetype='application/json') + + +# An HttpTrigger to generating EventHub event from azure-eventhub SDK. +# Events generated from azure-eventhub contain the full metadata. +@app.function_name(name="metadata_output") +@app.route(route="metadata_output") +async def metadata_output(req: func.HttpRequest): + # Parse event metadata from http request + json_string = req.get_body().decode('utf-8') + event_dict = json.loads(json_string) + + # Create an EventHub Client and event batch + client = EventHubProducerClient.from_connection_string( + os.getenv('AzureWebJobsEventHubConnectionString'), + eventhub_name='python-worker-ci-eventhub-one-metadata') + + # Generate new event based on http request with full metadata + event_data_batch = await client.create_batch() + event_data_batch.add(EventData(event_dict.get('body'))) + + # Send out event into event hub + try: + await client.send_batch(event_data_batch) + finally: + await client.close() + + return 'OK' + + +@app.function_name(name="metadata_trigger") +@app.event_hub_message_trigger( + arg_name="event", + event_hub_name="python-worker-ci-eventhub-one-metadata", + connection="AzureWebJobsEventHubConnectionString") +@app.blob_output(arg_name="$return", + path="python-worker-tests/test-metadata-triggered.txt", + connection="AzureWebJobsStorage") +async def metadata_trigger(event: func.EventHubEvent) -> bytes: + event_dict: typing.Mapping[str, typing.Any] = { + 'body': event.get_body().decode('utf-8'), + # Uncomment this when the EnqueuedTimeUtc is fixed in azure-functions + # 'enqueued_time': event.enqueued_time.isoformat(), + 'partition_key': event.partition_key, + 'sequence_number': event.sequence_number, + 'offset': event.offset, + 'metadata': event.metadata + } + + return json.dumps(event_dict) diff --git a/tests/endtoend/eventhub_functions/eventhub_functions_stein/generic/function_app.py b/tests/emulator_tests/eventhub_functions/eventhub_functions_stein/generic/function_app.py similarity index 100% rename from tests/endtoend/eventhub_functions/eventhub_functions_stein/generic/function_app.py rename to tests/emulator_tests/eventhub_functions/eventhub_functions_stein/generic/function_app.py diff --git a/tests/endtoend/eventhub_functions/eventhub_output/__init__.py b/tests/emulator_tests/eventhub_functions/eventhub_output/__init__.py similarity index 100% rename from tests/endtoend/eventhub_functions/eventhub_output/__init__.py rename to tests/emulator_tests/eventhub_functions/eventhub_output/__init__.py diff --git a/tests/endtoend/eventhub_functions/eventhub_output/function.json b/tests/emulator_tests/eventhub_functions/eventhub_output/function.json similarity index 100% rename from tests/endtoend/eventhub_functions/eventhub_output/function.json rename to tests/emulator_tests/eventhub_functions/eventhub_output/function.json diff --git a/tests/endtoend/eventhub_functions/eventhub_trigger/__init__.py b/tests/emulator_tests/eventhub_functions/eventhub_trigger/__init__.py similarity index 100% rename from tests/endtoend/eventhub_functions/eventhub_trigger/__init__.py rename to tests/emulator_tests/eventhub_functions/eventhub_trigger/__init__.py diff --git a/tests/endtoend/eventhub_functions/eventhub_trigger/function.json b/tests/emulator_tests/eventhub_functions/eventhub_trigger/function.json similarity index 100% rename from tests/endtoend/eventhub_functions/eventhub_trigger/function.json rename to tests/emulator_tests/eventhub_functions/eventhub_trigger/function.json diff --git a/tests/endtoend/eventhub_functions/get_eventhub_triggered/function.json b/tests/emulator_tests/eventhub_functions/get_eventhub_triggered/function.json similarity index 100% rename from tests/endtoend/eventhub_functions/get_eventhub_triggered/function.json rename to tests/emulator_tests/eventhub_functions/get_eventhub_triggered/function.json diff --git a/tests/endtoend/eventhub_functions/get_eventhub_triggered/main.py b/tests/emulator_tests/eventhub_functions/get_eventhub_triggered/main.py similarity index 100% rename from tests/endtoend/eventhub_functions/get_eventhub_triggered/main.py rename to tests/emulator_tests/eventhub_functions/get_eventhub_triggered/main.py diff --git a/tests/endtoend/eventhub_functions/get_metadata_triggered/__init__.py b/tests/emulator_tests/eventhub_functions/get_metadata_triggered/__init__.py similarity index 100% rename from tests/endtoend/eventhub_functions/get_metadata_triggered/__init__.py rename to tests/emulator_tests/eventhub_functions/get_metadata_triggered/__init__.py diff --git a/tests/endtoend/eventhub_functions/get_metadata_triggered/function.json b/tests/emulator_tests/eventhub_functions/get_metadata_triggered/function.json similarity index 100% rename from tests/endtoend/eventhub_functions/get_metadata_triggered/function.json rename to tests/emulator_tests/eventhub_functions/get_metadata_triggered/function.json diff --git a/tests/endtoend/eventhub_functions/metadata_output/__init__.py b/tests/emulator_tests/eventhub_functions/metadata_output/__init__.py similarity index 100% rename from tests/endtoend/eventhub_functions/metadata_output/__init__.py rename to tests/emulator_tests/eventhub_functions/metadata_output/__init__.py diff --git a/tests/endtoend/eventhub_functions/metadata_output/function.json b/tests/emulator_tests/eventhub_functions/metadata_output/function.json similarity index 100% rename from tests/endtoend/eventhub_functions/metadata_output/function.json rename to tests/emulator_tests/eventhub_functions/metadata_output/function.json diff --git a/tests/endtoend/eventhub_functions/metadata_trigger/__init__.py b/tests/emulator_tests/eventhub_functions/metadata_trigger/__init__.py similarity index 100% rename from tests/endtoend/eventhub_functions/metadata_trigger/__init__.py rename to tests/emulator_tests/eventhub_functions/metadata_trigger/__init__.py diff --git a/tests/endtoend/eventhub_functions/metadata_trigger/function.json b/tests/emulator_tests/eventhub_functions/metadata_trigger/function.json similarity index 100% rename from tests/endtoend/eventhub_functions/metadata_trigger/function.json rename to tests/emulator_tests/eventhub_functions/metadata_trigger/function.json diff --git a/tests/endtoend/generic_functions/generic_functions_stein/function_app.py b/tests/emulator_tests/generic_functions/generic_functions_stein/function_app.py similarity index 82% rename from tests/endtoend/generic_functions/generic_functions_stein/function_app.py rename to tests/emulator_tests/generic_functions/generic_functions_stein/function_app.py index 654148c9..2da6d44c 100644 --- a/tests/endtoend/generic_functions/generic_functions_stein/function_app.py +++ b/tests/emulator_tests/generic_functions/generic_functions_stein/function_app.py @@ -1,6 +1,8 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +import json import logging +import uuid import azure.functions as func @@ -15,7 +17,7 @@ arg_name="testEntity", type="table", connection="AzureWebJobsStorage", - table_name="EventHubBatchTest") + table_name="BindingTestTable") def return_processed_last(req: func.HttpRequest, testEntity): return func.HttpResponse(status_code=200) @@ -28,7 +30,7 @@ def return_processed_last(req: func.HttpRequest, testEntity): arg_name="testEntities", type="table", connection="AzureWebJobsStorage", - table_name="EventHubBatchTest") + table_name="BindingTestTable") def return_not_processed_last(req: func.HttpRequest, testEntities): return func.HttpResponse(status_code=200) @@ -41,7 +43,7 @@ def return_not_processed_last(req: func.HttpRequest, testEntities): arg_name="testEntity", type="table", connection="AzureWebJobsStorage", - table_name="EventHubBatchTest") + table_name="BindingTestTable") def mytimer(mytimer: func.TimerRequest, testEntity) -> None: logging.info("This timer trigger function executed successfully") @@ -54,7 +56,7 @@ def mytimer(mytimer: func.TimerRequest, testEntity) -> None: arg_name="testEntity", type="table", connection="AzureWebJobsStorage", - table_name="EventHubBatchTest") + table_name="BindingTestTable") def return_string(mytimer: func.TimerRequest, testEntity): logging.info("Return string") return "hi!" @@ -68,7 +70,7 @@ def return_string(mytimer: func.TimerRequest, testEntity): arg_name="testEntity", type="table", connection="AzureWebJobsStorage", - table_name="EventHubBatchTest") + table_name="BindingTestTable") def return_bytes(mytimer: func.TimerRequest, testEntity): logging.info("Return bytes") return "test-dată" @@ -82,7 +84,7 @@ def return_bytes(mytimer: func.TimerRequest, testEntity): arg_name="testEntity", type="table", connection="AzureWebJobsStorage", - table_name="EventHubBatchTest") + table_name="BindingTestTable") def return_dict(mytimer: func.TimerRequest, testEntity): logging.info("Return dict") return {"hello": "world"} @@ -96,7 +98,7 @@ def return_dict(mytimer: func.TimerRequest, testEntity): arg_name="testEntity", type="table", connection="AzureWebJobsStorage", - table_name="EventHubBatchTest") + table_name="BindingTestTable") def return_list(mytimer: func.TimerRequest, testEntity): logging.info("Return list") return [1, 2, 3] @@ -110,7 +112,7 @@ def return_list(mytimer: func.TimerRequest, testEntity): arg_name="testEntity", type="table", connection="AzureWebJobsStorage", - table_name="EventHubBatchTest") + table_name="BindingTestTable") def return_int(mytimer: func.TimerRequest, testEntity): logging.info("Return int") return 12 @@ -124,7 +126,7 @@ def return_int(mytimer: func.TimerRequest, testEntity): arg_name="testEntity", type="table", connection="AzureWebJobsStorage", - table_name="EventHubBatchTest") + table_name="BindingTestTable") def return_double(mytimer: func.TimerRequest, testEntity): logging.info("Return double") return 12.34 @@ -138,7 +140,20 @@ def return_double(mytimer: func.TimerRequest, testEntity): arg_name="testEntity", type="table", connection="AzureWebJobsStorage", - table_name="EventHubBatchTest") + table_name="BindingTestTable") def return_bool(mytimer: func.TimerRequest, testEntity): logging.info("Return bool") return True + + +@app.function_name(name="table_out_binding") +@app.route(route="table_out_binding", binding_arg_name="resp") +@app.table_output(arg_name="$return", + connection="AzureWebJobsStorage", + table_name="BindingTestTable") +def table_out_binding(req: func.HttpRequest, resp: func.Out[func.HttpResponse]): + row_key_uuid = str(uuid.uuid4()) + table_dict = {'PartitionKey': 'test', 'RowKey': row_key_uuid} + table_json = json.dumps(table_dict) + resp.set(table_json) + return table_json diff --git a/tests/endtoend/generic_functions/return_bool/function.json b/tests/emulator_tests/generic_functions/return_bool/function.json similarity index 100% rename from tests/endtoend/generic_functions/return_bool/function.json rename to tests/emulator_tests/generic_functions/return_bool/function.json diff --git a/tests/endtoend/generic_functions/return_bool/main.py b/tests/emulator_tests/generic_functions/return_bool/main.py similarity index 100% rename from tests/endtoend/generic_functions/return_bool/main.py rename to tests/emulator_tests/generic_functions/return_bool/main.py diff --git a/tests/endtoend/generic_functions/return_bytes/function.json b/tests/emulator_tests/generic_functions/return_bytes/function.json similarity index 100% rename from tests/endtoend/generic_functions/return_bytes/function.json rename to tests/emulator_tests/generic_functions/return_bytes/function.json diff --git a/tests/endtoend/generic_functions/return_bytes/main.py b/tests/emulator_tests/generic_functions/return_bytes/main.py similarity index 100% rename from tests/endtoend/generic_functions/return_bytes/main.py rename to tests/emulator_tests/generic_functions/return_bytes/main.py diff --git a/tests/endtoend/generic_functions/return_dict/function.json b/tests/emulator_tests/generic_functions/return_dict/function.json similarity index 100% rename from tests/endtoend/generic_functions/return_dict/function.json rename to tests/emulator_tests/generic_functions/return_dict/function.json diff --git a/tests/endtoend/generic_functions/return_dict/main.py b/tests/emulator_tests/generic_functions/return_dict/main.py similarity index 100% rename from tests/endtoend/generic_functions/return_dict/main.py rename to tests/emulator_tests/generic_functions/return_dict/main.py diff --git a/tests/endtoend/generic_functions/return_double/function.json b/tests/emulator_tests/generic_functions/return_double/function.json similarity index 100% rename from tests/endtoend/generic_functions/return_double/function.json rename to tests/emulator_tests/generic_functions/return_double/function.json diff --git a/tests/endtoend/generic_functions/return_double/main.py b/tests/emulator_tests/generic_functions/return_double/main.py similarity index 100% rename from tests/endtoend/generic_functions/return_double/main.py rename to tests/emulator_tests/generic_functions/return_double/main.py diff --git a/tests/endtoend/generic_functions/return_int/function.json b/tests/emulator_tests/generic_functions/return_int/function.json similarity index 100% rename from tests/endtoend/generic_functions/return_int/function.json rename to tests/emulator_tests/generic_functions/return_int/function.json diff --git a/tests/endtoend/generic_functions/return_int/main.py b/tests/emulator_tests/generic_functions/return_int/main.py similarity index 100% rename from tests/endtoend/generic_functions/return_int/main.py rename to tests/emulator_tests/generic_functions/return_int/main.py diff --git a/tests/endtoend/generic_functions/return_list/function.json b/tests/emulator_tests/generic_functions/return_list/function.json similarity index 100% rename from tests/endtoend/generic_functions/return_list/function.json rename to tests/emulator_tests/generic_functions/return_list/function.json diff --git a/tests/endtoend/generic_functions/return_list/main.py b/tests/emulator_tests/generic_functions/return_list/main.py similarity index 100% rename from tests/endtoend/generic_functions/return_list/main.py rename to tests/emulator_tests/generic_functions/return_list/main.py diff --git a/tests/endtoend/generic_functions/return_none/function.json b/tests/emulator_tests/generic_functions/return_none/function.json similarity index 100% rename from tests/endtoend/generic_functions/return_none/function.json rename to tests/emulator_tests/generic_functions/return_none/function.json diff --git a/tests/endtoend/generic_functions/return_none/main.py b/tests/emulator_tests/generic_functions/return_none/main.py similarity index 100% rename from tests/endtoend/generic_functions/return_none/main.py rename to tests/emulator_tests/generic_functions/return_none/main.py diff --git a/tests/endtoend/generic_functions/return_none_no_type_hint/function.json b/tests/emulator_tests/generic_functions/return_none_no_type_hint/function.json similarity index 100% rename from tests/endtoend/generic_functions/return_none_no_type_hint/function.json rename to tests/emulator_tests/generic_functions/return_none_no_type_hint/function.json diff --git a/tests/endtoend/generic_functions/return_none_no_type_hint/main.py b/tests/emulator_tests/generic_functions/return_none_no_type_hint/main.py similarity index 100% rename from tests/endtoend/generic_functions/return_none_no_type_hint/main.py rename to tests/emulator_tests/generic_functions/return_none_no_type_hint/main.py diff --git a/tests/endtoend/generic_functions/return_not_processed_last/__init__.py b/tests/emulator_tests/generic_functions/return_not_processed_last/__init__.py similarity index 100% rename from tests/endtoend/generic_functions/return_not_processed_last/__init__.py rename to tests/emulator_tests/generic_functions/return_not_processed_last/__init__.py diff --git a/tests/endtoend/generic_functions/return_not_processed_last/function.json b/tests/emulator_tests/generic_functions/return_not_processed_last/function.json similarity index 91% rename from tests/endtoend/generic_functions/return_not_processed_last/function.json rename to tests/emulator_tests/generic_functions/return_not_processed_last/function.json index 66d1e80e..e02ae4d1 100644 --- a/tests/endtoend/generic_functions/return_not_processed_last/function.json +++ b/tests/emulator_tests/generic_functions/return_not_processed_last/function.json @@ -14,7 +14,7 @@ "direction": "in", "type": "table", "name": "testEntities", - "tableName": "EventHubBatchTest", + "tableName": "BindingTestTable", "connection": "AzureWebJobsStorage" }, { diff --git a/tests/endtoend/generic_functions/return_processed_last/__init__.py b/tests/emulator_tests/generic_functions/return_processed_last/__init__.py similarity index 100% rename from tests/endtoend/generic_functions/return_processed_last/__init__.py rename to tests/emulator_tests/generic_functions/return_processed_last/__init__.py diff --git a/tests/endtoend/generic_functions/return_processed_last/function.json b/tests/emulator_tests/generic_functions/return_processed_last/function.json similarity index 91% rename from tests/endtoend/generic_functions/return_processed_last/function.json rename to tests/emulator_tests/generic_functions/return_processed_last/function.json index 82ac266a..d23f01a8 100644 --- a/tests/endtoend/generic_functions/return_processed_last/function.json +++ b/tests/emulator_tests/generic_functions/return_processed_last/function.json @@ -14,7 +14,7 @@ "direction": "in", "type": "table", "name": "testEntity", - "tableName": "EventHubBatchTest", + "tableName": "BindingTestTable", "connection": "AzureWebJobsStorage" }, { diff --git a/tests/endtoend/generic_functions/return_string/function.json b/tests/emulator_tests/generic_functions/return_string/function.json similarity index 100% rename from tests/endtoend/generic_functions/return_string/function.json rename to tests/emulator_tests/generic_functions/return_string/function.json diff --git a/tests/endtoend/generic_functions/return_string/main.py b/tests/emulator_tests/generic_functions/return_string/main.py similarity index 100% rename from tests/endtoend/generic_functions/return_string/main.py rename to tests/emulator_tests/generic_functions/return_string/main.py diff --git a/tests/emulator_tests/generic_functions/table_out_binding/__init__.py b/tests/emulator_tests/generic_functions/table_out_binding/__init__.py new file mode 100644 index 00000000..09c7058e --- /dev/null +++ b/tests/emulator_tests/generic_functions/table_out_binding/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import json +import uuid +import azure.functions as func + + +def main(req: func.HttpRequest, resp: func.Out[func.HttpResponse]): + row_key_uuid = str(uuid.uuid4()) + table_dict = {'PartitionKey': 'test', 'RowKey': row_key_uuid} + table_json = json.dumps(table_dict) + resp.set(table_json) + return table_json diff --git a/tests/emulator_tests/generic_functions/table_out_binding/function.json b/tests/emulator_tests/generic_functions/table_out_binding/function.json new file mode 100644 index 00000000..25537873 --- /dev/null +++ b/tests/emulator_tests/generic_functions/table_out_binding/function.json @@ -0,0 +1,24 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "type": "httpTrigger", + "direction": "in", + "authLevel": "anonymous", + "methods": ["post"], + "name": "req" + }, + { + "direction": "out", + "type": "table", + "name": "$return", + "tableName": "BindingTestTable", + "connection": "AzureWebJobsStorage" + }, + { + "name": "resp", + "type": "http", + "direction": "out" + } + ] + } \ No newline at end of file diff --git a/tests/endtoend/queue_functions/get_queue_blob/function.json b/tests/emulator_tests/queue_functions/get_queue_blob/function.json similarity index 100% rename from tests/endtoend/queue_functions/get_queue_blob/function.json rename to tests/emulator_tests/queue_functions/get_queue_blob/function.json diff --git a/tests/endtoend/queue_functions/get_queue_blob/main.py b/tests/emulator_tests/queue_functions/get_queue_blob/main.py similarity index 100% rename from tests/endtoend/queue_functions/get_queue_blob/main.py rename to tests/emulator_tests/queue_functions/get_queue_blob/main.py diff --git a/tests/endtoend/queue_functions/get_queue_blob_message_return/function.json b/tests/emulator_tests/queue_functions/get_queue_blob_message_return/function.json similarity index 100% rename from tests/endtoend/queue_functions/get_queue_blob_message_return/function.json rename to tests/emulator_tests/queue_functions/get_queue_blob_message_return/function.json diff --git a/tests/endtoend/queue_functions/get_queue_blob_message_return/main.py b/tests/emulator_tests/queue_functions/get_queue_blob_message_return/main.py similarity index 100% rename from tests/endtoend/queue_functions/get_queue_blob_message_return/main.py rename to tests/emulator_tests/queue_functions/get_queue_blob_message_return/main.py diff --git a/tests/endtoend/queue_functions/get_queue_blob_return/function.json b/tests/emulator_tests/queue_functions/get_queue_blob_return/function.json similarity index 100% rename from tests/endtoend/queue_functions/get_queue_blob_return/function.json rename to tests/emulator_tests/queue_functions/get_queue_blob_return/function.json diff --git a/tests/endtoend/queue_functions/get_queue_blob_return/main.py b/tests/emulator_tests/queue_functions/get_queue_blob_return/main.py similarity index 100% rename from tests/endtoend/queue_functions/get_queue_blob_return/main.py rename to tests/emulator_tests/queue_functions/get_queue_blob_return/main.py diff --git a/tests/endtoend/queue_functions/get_queue_untyped_blob_return/function.json b/tests/emulator_tests/queue_functions/get_queue_untyped_blob_return/function.json similarity index 100% rename from tests/endtoend/queue_functions/get_queue_untyped_blob_return/function.json rename to tests/emulator_tests/queue_functions/get_queue_untyped_blob_return/function.json diff --git a/tests/endtoend/queue_functions/get_queue_untyped_blob_return/main.py b/tests/emulator_tests/queue_functions/get_queue_untyped_blob_return/main.py similarity index 100% rename from tests/endtoend/queue_functions/get_queue_untyped_blob_return/main.py rename to tests/emulator_tests/queue_functions/get_queue_untyped_blob_return/main.py diff --git a/tests/endtoend/queue_functions/put_queue/function.json b/tests/emulator_tests/queue_functions/put_queue/function.json similarity index 100% rename from tests/endtoend/queue_functions/put_queue/function.json rename to tests/emulator_tests/queue_functions/put_queue/function.json diff --git a/tests/endtoend/queue_functions/put_queue/main.py b/tests/emulator_tests/queue_functions/put_queue/main.py similarity index 100% rename from tests/endtoend/queue_functions/put_queue/main.py rename to tests/emulator_tests/queue_functions/put_queue/main.py diff --git a/tests/endtoend/queue_functions/put_queue_message_return/function.json b/tests/emulator_tests/queue_functions/put_queue_message_return/function.json similarity index 100% rename from tests/endtoend/queue_functions/put_queue_message_return/function.json rename to tests/emulator_tests/queue_functions/put_queue_message_return/function.json diff --git a/tests/endtoend/queue_functions/put_queue_message_return/main.py b/tests/emulator_tests/queue_functions/put_queue_message_return/main.py similarity index 100% rename from tests/endtoend/queue_functions/put_queue_message_return/main.py rename to tests/emulator_tests/queue_functions/put_queue_message_return/main.py diff --git a/tests/endtoend/queue_functions/put_queue_multiple_out/function.json b/tests/emulator_tests/queue_functions/put_queue_multiple_out/function.json similarity index 100% rename from tests/endtoend/queue_functions/put_queue_multiple_out/function.json rename to tests/emulator_tests/queue_functions/put_queue_multiple_out/function.json diff --git a/tests/endtoend/queue_functions/put_queue_multiple_out/main.py b/tests/emulator_tests/queue_functions/put_queue_multiple_out/main.py similarity index 100% rename from tests/endtoend/queue_functions/put_queue_multiple_out/main.py rename to tests/emulator_tests/queue_functions/put_queue_multiple_out/main.py diff --git a/tests/endtoend/queue_functions/put_queue_return/function.json b/tests/emulator_tests/queue_functions/put_queue_return/function.json similarity index 100% rename from tests/endtoend/queue_functions/put_queue_return/function.json rename to tests/emulator_tests/queue_functions/put_queue_return/function.json diff --git a/tests/endtoend/queue_functions/put_queue_return/main.py b/tests/emulator_tests/queue_functions/put_queue_return/main.py similarity index 100% rename from tests/endtoend/queue_functions/put_queue_return/main.py rename to tests/emulator_tests/queue_functions/put_queue_return/main.py diff --git a/tests/endtoend/queue_functions/put_queue_return_multiple/function.json b/tests/emulator_tests/queue_functions/put_queue_return_multiple/function.json similarity index 100% rename from tests/endtoend/queue_functions/put_queue_return_multiple/function.json rename to tests/emulator_tests/queue_functions/put_queue_return_multiple/function.json diff --git a/tests/endtoend/queue_functions/put_queue_return_multiple/main.py b/tests/emulator_tests/queue_functions/put_queue_return_multiple/main.py similarity index 100% rename from tests/endtoend/queue_functions/put_queue_return_multiple/main.py rename to tests/emulator_tests/queue_functions/put_queue_return_multiple/main.py diff --git a/tests/endtoend/queue_functions/put_queue_untyped_return/function.json b/tests/emulator_tests/queue_functions/put_queue_untyped_return/function.json similarity index 100% rename from tests/endtoend/queue_functions/put_queue_untyped_return/function.json rename to tests/emulator_tests/queue_functions/put_queue_untyped_return/function.json diff --git a/tests/endtoend/queue_functions/put_queue_untyped_return/main.py b/tests/emulator_tests/queue_functions/put_queue_untyped_return/main.py similarity index 100% rename from tests/endtoend/queue_functions/put_queue_untyped_return/main.py rename to tests/emulator_tests/queue_functions/put_queue_untyped_return/main.py diff --git a/tests/endtoend/queue_functions/queue_functions_stein/function_app.py b/tests/emulator_tests/queue_functions/queue_functions_stein/function_app.py similarity index 97% rename from tests/endtoend/queue_functions/queue_functions_stein/function_app.py rename to tests/emulator_tests/queue_functions/queue_functions_stein/function_app.py index 0b883fb1..087cf459 100644 --- a/tests/endtoend/queue_functions/queue_functions_stein/function_app.py +++ b/tests/emulator_tests/queue_functions/queue_functions_stein/function_app.py @@ -1,185 +1,185 @@ -import json -import logging -import typing - -import azure.functions as func - -app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) - - -@app.function_name(name="get_queue_blob") -@app.route(route="get_queue_blob") -@app.blob_input(arg_name="file", - connection="AzureWebJobsStorage", - path="python-worker-tests/test-queue-blob.txt") -def get_queue_blob(req: func.HttpRequest, file: func.InputStream) -> str: - return json.dumps({ - 'queue': json.loads(file.read().decode('utf-8')) - }) - - -@app.function_name(name="get_queue_blob_message_return") -@app.route(route="get_queue_blob_message_return") -@app.blob_input(arg_name="file", - connection="AzureWebJobsStorage", - path="python-worker-tests/test-queue-blob-message-return.txt") -def get_queue_blob_message_return(req: func.HttpRequest, - file: func.InputStream) -> str: - return file.read().decode('utf-8') - - -@app.function_name(name="get_queue_blob_return") -@app.route(route="get_queue_blob_return") -@app.blob_input(arg_name="file", - connection="AzureWebJobsStorage", - path="python-worker-tests/test-queue-blob-return.txt") -def get_queue_blob_return(req: func.HttpRequest, file: func.InputStream) -> str: - return file.read().decode('utf-8') - - -@app.function_name(name="get_queue_untyped_blob_return") -@app.route(route="get_queue_untyped_blob_return") -@app.blob_input(arg_name="file", - connection="AzureWebJobsStorage", - path="python-worker-tests/test-queue-untyped-blob-return.txt") -def get_queue_untyped_blob_return(req: func.HttpRequest, - file: func.InputStream) -> str: - return file.read().decode('utf-8') - - -@app.function_name(name="put_queue") -@app.route(route="put_queue") -@app.queue_output(arg_name="msg", - connection="AzureWebJobsStorage", - queue_name="testqueue") -def put_queue(req: func.HttpRequest, msg: func.Out[str]): - msg.set(req.get_body()) - - return 'OK' - - -@app.function_name(name="put_queue_message_return") -@app.route(route="put_queue_message_return", binding_arg_name="resp") -@app.queue_output(arg_name="$return", - connection="AzureWebJobsStorage", - queue_name="testqueue-message-return") -def main(req: func.HttpRequest, resp: func.Out[str]) -> bytes: - return func.QueueMessage(body=req.get_body()) - - -@app.function_name("put_queue_multiple_out") -@app.route(route="put_queue_multiple_out", binding_arg_name="resp") -@app.queue_output(arg_name="msg", - connection="AzureWebJobsStorage", - queue_name="testqueue-return-multiple-outparam") -def put_queue_multiple_out(req: func.HttpRequest, - resp: func.Out[func.HttpResponse], - msg: func.Out[func.QueueMessage]) -> None: - data = req.get_body().decode() - msg.set(func.QueueMessage(body=data)) - resp.set(func.HttpResponse(body='HTTP response: {}'.format(data))) - - -@app.function_name("put_queue_return") -@app.route(route="put_queue_return", binding_arg_name="resp") -@app.queue_output(arg_name="$return", - connection="AzureWebJobsStorage", - queue_name="testqueue-return") -def put_queue_return(req: func.HttpRequest, resp: func.Out[str]) -> bytes: - return req.get_body() - - -@app.function_name(name="put_queue_multiple_return") -@app.route(route="put_queue_multiple_return") -@app.queue_output(arg_name="msgs", - connection="AzureWebJobsStorage", - queue_name="testqueue-return-multiple") -def put_queue_multiple_return(req: func.HttpRequest, - msgs: func.Out[typing.List[str]]): - msgs.set(['one', 'two']) - - -@app.function_name(name="put_queue_untyped_return") -@app.route(route="put_queue_untyped_return", binding_arg_name="resp") -@app.queue_output(arg_name="$return", - connection="AzureWebJobsStorage", - queue_name="testqueue-untyped-return") -def put_queue_untyped_return(req: func.HttpRequest, - resp: func.Out[str]) -> bytes: - return func.QueueMessage(body=req.get_body()) - - -@app.function_name(name="queue_trigger") -@app.queue_trigger(arg_name="msg", - queue_name="testqueue", - connection="AzureWebJobsStorage") -@app.blob_output(arg_name="$return", - connection="AzureWebJobsStorage", - path="python-worker-tests/test-queue-blob.txt") -def queue_trigger(msg: func.QueueMessage) -> str: - result = json.dumps({ - 'id': msg.id, - 'body': msg.get_body().decode('utf-8'), - 'expiration_time': (msg.expiration_time.isoformat() - if msg.expiration_time else None), - 'insertion_time': (msg.insertion_time.isoformat() - if msg.insertion_time else None), - 'time_next_visible': (msg.time_next_visible.isoformat() - if msg.time_next_visible else None), - 'pop_receipt': msg.pop_receipt, - 'dequeue_count': msg.dequeue_count - }) - - return result - - -@app.function_name(name="queue_trigger_message_return") -@app.queue_trigger(arg_name="msg", - queue_name="testqueue-message-return", - connection="AzureWebJobsStorage") -@app.blob_output(arg_name="$return", - connection="AzureWebJobsStorage", - path="python-worker-tests/test-queue-blob-message-return.txt") -def queue_trigger_message_return(msg: func.QueueMessage) -> bytes: - return msg.get_body() - - -@app.function_name(name="queue_trigger_return") -@app.queue_trigger(arg_name="msg", - queue_name="testqueue-return", - connection="AzureWebJobsStorage") -@app.blob_output(arg_name="$return", - connection="AzureWebJobsStorage", - path="python-worker-tests/test-queue-blob-return.txt") -def queue_trigger_return(msg: func.QueueMessage) -> bytes: - return msg.get_body() - - -@app.function_name(name="queue_trigger_return_multiple") -@app.queue_trigger(arg_name="msg", - queue_name="testqueue-return-multiple", - connection="AzureWebJobsStorage") -def queue_trigger_return_multiple(msg: func.QueueMessage) -> None: - logging.info('trigger on message: %s', msg.get_body().decode('utf-8')) - - -@app.function_name(name="queue_trigger_untyped") -@app.queue_trigger(arg_name="msg", - queue_name="testqueue-untyped-return", - connection="AzureWebJobsStorage") -@app.blob_output(arg_name="$return", - connection="AzureWebJobsStorage", - path="python-worker-tests/test-queue-untyped-blob-return.txt") -def queue_trigger_untyped(msg: str) -> str: - return msg - - -@app.function_name(name="put_queue_return_multiple") -@app.route(route="put_queue_return_multiple", binding_arg_name="resp") -@app.queue_output(arg_name="msgs", - connection="AzureWebJobsStorage", - queue_name="testqueue-return-multiple") -def put_queue_return_multiple(req: func.HttpRequest, - resp: func.Out[str], - msgs: func.Out[typing.List[str]]): - msgs.set(['one', 'two']) +import json +import logging +import typing + +import azure.functions as func + +app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) + + +@app.function_name(name="get_queue_blob") +@app.route(route="get_queue_blob") +@app.blob_input(arg_name="file", + connection="AzureWebJobsStorage", + path="python-worker-tests/test-queue-blob.txt") +def get_queue_blob(req: func.HttpRequest, file: func.InputStream) -> str: + return json.dumps({ + 'queue': json.loads(file.read().decode('utf-8')) + }) + + +@app.function_name(name="get_queue_blob_message_return") +@app.route(route="get_queue_blob_message_return") +@app.blob_input(arg_name="file", + connection="AzureWebJobsStorage", + path="python-worker-tests/test-queue-blob-message-return.txt") +def get_queue_blob_message_return(req: func.HttpRequest, + file: func.InputStream) -> str: + return file.read().decode('utf-8') + + +@app.function_name(name="get_queue_blob_return") +@app.route(route="get_queue_blob_return") +@app.blob_input(arg_name="file", + connection="AzureWebJobsStorage", + path="python-worker-tests/test-queue-blob-return.txt") +def get_queue_blob_return(req: func.HttpRequest, file: func.InputStream) -> str: + return file.read().decode('utf-8') + + +@app.function_name(name="get_queue_untyped_blob_return") +@app.route(route="get_queue_untyped_blob_return") +@app.blob_input(arg_name="file", + connection="AzureWebJobsStorage", + path="python-worker-tests/test-queue-untyped-blob-return.txt") +def get_queue_untyped_blob_return(req: func.HttpRequest, + file: func.InputStream) -> str: + return file.read().decode('utf-8') + + +@app.function_name(name="put_queue") +@app.route(route="put_queue") +@app.queue_output(arg_name="msg", + connection="AzureWebJobsStorage", + queue_name="testqueue") +def put_queue(req: func.HttpRequest, msg: func.Out[str]): + msg.set(req.get_body()) + + return 'OK' + + +@app.function_name(name="put_queue_message_return") +@app.route(route="put_queue_message_return", binding_arg_name="resp") +@app.queue_output(arg_name="$return", + connection="AzureWebJobsStorage", + queue_name="testqueue-message-return") +def main(req: func.HttpRequest, resp: func.Out[str]) -> bytes: + return func.QueueMessage(body=req.get_body()) + + +@app.function_name("put_queue_multiple_out") +@app.route(route="put_queue_multiple_out", binding_arg_name="resp") +@app.queue_output(arg_name="msg", + connection="AzureWebJobsStorage", + queue_name="testqueue-return-multiple-outparam") +def put_queue_multiple_out(req: func.HttpRequest, + resp: func.Out[func.HttpResponse], + msg: func.Out[func.QueueMessage]) -> None: + data = req.get_body().decode() + msg.set(func.QueueMessage(body=data)) + resp.set(func.HttpResponse(body='HTTP response: {}'.format(data))) + + +@app.function_name("put_queue_return") +@app.route(route="put_queue_return", binding_arg_name="resp") +@app.queue_output(arg_name="$return", + connection="AzureWebJobsStorage", + queue_name="testqueue-return") +def put_queue_return(req: func.HttpRequest, resp: func.Out[str]) -> bytes: + return req.get_body() + + +@app.function_name(name="put_queue_multiple_return") +@app.route(route="put_queue_multiple_return") +@app.queue_output(arg_name="msgs", + connection="AzureWebJobsStorage", + queue_name="testqueue-return-multiple") +def put_queue_multiple_return(req: func.HttpRequest, + msgs: func.Out[typing.List[str]]): + msgs.set(['one', 'two']) + + +@app.function_name(name="put_queue_untyped_return") +@app.route(route="put_queue_untyped_return", binding_arg_name="resp") +@app.queue_output(arg_name="$return", + connection="AzureWebJobsStorage", + queue_name="testqueue-untyped-return") +def put_queue_untyped_return(req: func.HttpRequest, + resp: func.Out[str]) -> bytes: + return func.QueueMessage(body=req.get_body()) + + +@app.function_name(name="queue_trigger") +@app.queue_trigger(arg_name="msg", + queue_name="testqueue", + connection="AzureWebJobsStorage") +@app.blob_output(arg_name="$return", + connection="AzureWebJobsStorage", + path="python-worker-tests/test-queue-blob.txt") +def queue_trigger(msg: func.QueueMessage) -> str: + result = json.dumps({ + 'id': msg.id, + 'body': msg.get_body().decode('utf-8'), + 'expiration_time': (msg.expiration_time.isoformat() + if msg.expiration_time else None), + 'insertion_time': (msg.insertion_time.isoformat() + if msg.insertion_time else None), + 'time_next_visible': (msg.time_next_visible.isoformat() + if msg.time_next_visible else None), + 'pop_receipt': msg.pop_receipt, + 'dequeue_count': msg.dequeue_count + }) + + return result + + +@app.function_name(name="queue_trigger_message_return") +@app.queue_trigger(arg_name="msg", + queue_name="testqueue-message-return", + connection="AzureWebJobsStorage") +@app.blob_output(arg_name="$return", + connection="AzureWebJobsStorage", + path="python-worker-tests/test-queue-blob-message-return.txt") +def queue_trigger_message_return(msg: func.QueueMessage) -> bytes: + return msg.get_body() + + +@app.function_name(name="queue_trigger_return") +@app.queue_trigger(arg_name="msg", + queue_name="testqueue-return", + connection="AzureWebJobsStorage") +@app.blob_output(arg_name="$return", + connection="AzureWebJobsStorage", + path="python-worker-tests/test-queue-blob-return.txt") +def queue_trigger_return(msg: func.QueueMessage) -> bytes: + return msg.get_body() + + +@app.function_name(name="queue_trigger_return_multiple") +@app.queue_trigger(arg_name="msg", + queue_name="testqueue-return-multiple", + connection="AzureWebJobsStorage") +def queue_trigger_return_multiple(msg: func.QueueMessage) -> None: + logging.info('trigger on message: %s', msg.get_body().decode('utf-8')) + + +@app.function_name(name="queue_trigger_untyped") +@app.queue_trigger(arg_name="msg", + queue_name="testqueue-untyped-return", + connection="AzureWebJobsStorage") +@app.blob_output(arg_name="$return", + connection="AzureWebJobsStorage", + path="python-worker-tests/test-queue-untyped-blob-return.txt") +def queue_trigger_untyped(msg: str) -> str: + return msg + + +@app.function_name(name="put_queue_return_multiple") +@app.route(route="put_queue_return_multiple", binding_arg_name="resp") +@app.queue_output(arg_name="msgs", + connection="AzureWebJobsStorage", + queue_name="testqueue-return-multiple") +def put_queue_return_multiple(req: func.HttpRequest, + resp: func.Out[str], + msgs: func.Out[typing.List[str]]): + msgs.set(['one', 'two']) diff --git a/tests/endtoend/queue_functions/queue_functions_stein/generic/function_app.py b/tests/emulator_tests/queue_functions/queue_functions_stein/generic/function_app.py similarity index 100% rename from tests/endtoend/queue_functions/queue_functions_stein/generic/function_app.py rename to tests/emulator_tests/queue_functions/queue_functions_stein/generic/function_app.py diff --git a/tests/endtoend/queue_functions/queue_trigger/function.json b/tests/emulator_tests/queue_functions/queue_trigger/function.json similarity index 100% rename from tests/endtoend/queue_functions/queue_trigger/function.json rename to tests/emulator_tests/queue_functions/queue_trigger/function.json diff --git a/tests/endtoend/queue_functions/queue_trigger/main.py b/tests/emulator_tests/queue_functions/queue_trigger/main.py similarity index 100% rename from tests/endtoend/queue_functions/queue_trigger/main.py rename to tests/emulator_tests/queue_functions/queue_trigger/main.py diff --git a/tests/endtoend/queue_functions/queue_trigger_message_return/function.json b/tests/emulator_tests/queue_functions/queue_trigger_message_return/function.json similarity index 100% rename from tests/endtoend/queue_functions/queue_trigger_message_return/function.json rename to tests/emulator_tests/queue_functions/queue_trigger_message_return/function.json diff --git a/tests/endtoend/queue_functions/queue_trigger_message_return/main.py b/tests/emulator_tests/queue_functions/queue_trigger_message_return/main.py similarity index 100% rename from tests/endtoend/queue_functions/queue_trigger_message_return/main.py rename to tests/emulator_tests/queue_functions/queue_trigger_message_return/main.py diff --git a/tests/endtoend/queue_functions/queue_trigger_return/function.json b/tests/emulator_tests/queue_functions/queue_trigger_return/function.json similarity index 100% rename from tests/endtoend/queue_functions/queue_trigger_return/function.json rename to tests/emulator_tests/queue_functions/queue_trigger_return/function.json diff --git a/tests/endtoend/queue_functions/queue_trigger_return/main.py b/tests/emulator_tests/queue_functions/queue_trigger_return/main.py similarity index 100% rename from tests/endtoend/queue_functions/queue_trigger_return/main.py rename to tests/emulator_tests/queue_functions/queue_trigger_return/main.py diff --git a/tests/endtoend/queue_functions/queue_trigger_return_multiple/function.json b/tests/emulator_tests/queue_functions/queue_trigger_return_multiple/function.json similarity index 100% rename from tests/endtoend/queue_functions/queue_trigger_return_multiple/function.json rename to tests/emulator_tests/queue_functions/queue_trigger_return_multiple/function.json diff --git a/tests/endtoend/queue_functions/queue_trigger_return_multiple/main.py b/tests/emulator_tests/queue_functions/queue_trigger_return_multiple/main.py similarity index 100% rename from tests/endtoend/queue_functions/queue_trigger_return_multiple/main.py rename to tests/emulator_tests/queue_functions/queue_trigger_return_multiple/main.py diff --git a/tests/endtoend/queue_functions/queue_trigger_untyped/function.json b/tests/emulator_tests/queue_functions/queue_trigger_untyped/function.json similarity index 100% rename from tests/endtoend/queue_functions/queue_trigger_untyped/function.json rename to tests/emulator_tests/queue_functions/queue_trigger_untyped/function.json diff --git a/tests/endtoend/queue_functions/queue_trigger_untyped/main.py b/tests/emulator_tests/queue_functions/queue_trigger_untyped/main.py similarity index 100% rename from tests/endtoend/queue_functions/queue_trigger_untyped/main.py rename to tests/emulator_tests/queue_functions/queue_trigger_untyped/main.py diff --git a/tests/endtoend/table_functions/table_functions_stein/function_app.py b/tests/emulator_tests/table_functions/table_functions_stein/function_app.py similarity index 100% rename from tests/endtoend/table_functions/table_functions_stein/function_app.py rename to tests/emulator_tests/table_functions/table_functions_stein/function_app.py diff --git a/tests/endtoend/table_functions/table_functions_stein/generic/function_app.py b/tests/emulator_tests/table_functions/table_functions_stein/generic/function_app.py similarity index 100% rename from tests/endtoend/table_functions/table_functions_stein/generic/function_app.py rename to tests/emulator_tests/table_functions/table_functions_stein/generic/function_app.py diff --git a/tests/endtoend/table_functions/table_in_binding/__init__.py b/tests/emulator_tests/table_functions/table_in_binding/__init__.py similarity index 100% rename from tests/endtoend/table_functions/table_in_binding/__init__.py rename to tests/emulator_tests/table_functions/table_in_binding/__init__.py diff --git a/tests/endtoend/table_functions/table_in_binding/function.json b/tests/emulator_tests/table_functions/table_in_binding/function.json similarity index 100% rename from tests/endtoend/table_functions/table_in_binding/function.json rename to tests/emulator_tests/table_functions/table_in_binding/function.json diff --git a/tests/endtoend/table_functions/table_out_binding/__init__.py b/tests/emulator_tests/table_functions/table_out_binding/__init__.py similarity index 100% rename from tests/endtoend/table_functions/table_out_binding/__init__.py rename to tests/emulator_tests/table_functions/table_out_binding/__init__.py diff --git a/tests/endtoend/table_functions/table_out_binding/function.json b/tests/emulator_tests/table_functions/table_out_binding/function.json similarity index 100% rename from tests/endtoend/table_functions/table_out_binding/function.json rename to tests/emulator_tests/table_functions/table_out_binding/function.json diff --git a/tests/endtoend/test_blob_functions.py b/tests/emulator_tests/test_blob_functions.py similarity index 95% rename from tests/endtoend/test_blob_functions.py rename to tests/emulator_tests/test_blob_functions.py index 4d51693e..d6a840a3 100644 --- a/tests/endtoend/test_blob_functions.py +++ b/tests/emulator_tests/test_blob_functions.py @@ -10,7 +10,7 @@ class TestBlobFunctions(testutils.WebHostTestCase): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'blob_functions' + return testutils.EMULATOR_TESTS_FOLDER / 'blob_functions' @testutils.retryable_test(3, 5) def test_blob_io_str(self): @@ -154,13 +154,13 @@ class TestBlobFunctionsStein(TestBlobFunctions): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'blob_functions' / \ - 'blob_functions_stein' + return testutils.EMULATOR_TESTS_FOLDER / 'blob_functions' / \ + 'blob_functions_stein' class TestBlobFunctionsSteinGeneric(TestBlobFunctions): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'blob_functions' / \ + return testutils.EMULATOR_TESTS_FOLDER / 'blob_functions' / \ 'blob_functions_stein' / 'generic' diff --git a/tests/endtoend/test_eventhub_batch_functions.py b/tests/emulator_tests/test_eventhub_batch_functions.py similarity index 94% rename from tests/endtoend/test_eventhub_batch_functions.py rename to tests/emulator_tests/test_eventhub_batch_functions.py index 11cf4d38..1a8ae2a9 100644 --- a/tests/endtoend/test_eventhub_batch_functions.py +++ b/tests/emulator_tests/test_eventhub_batch_functions.py @@ -1,8 +1,10 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. import json +import sys import time from datetime import datetime +from unittest.case import skipIf from dateutil import parser from tests.utils import testutils @@ -19,7 +21,7 @@ class TestEventHubFunctions(testutils.WebHostTestCase): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'eventhub_batch_functions' + return testutils.EMULATOR_TESTS_FOLDER / 'eventhub_batch_functions' @classmethod def get_libraries_to_install(cls): @@ -64,6 +66,9 @@ def test_eventhub_multiple(self): self.assertDictEqual(all_row_keys_seen, row_keys_seen) + @skipIf(sys.version_info.minor == 7, + "Using azure-eventhub SDK with the EventHub Emulator" + "requires Python 3.8+") @testutils.retryable_test(3, 5) def test_eventhub_multiple_with_metadata(self): # Generate a unique event body for EventHub event @@ -130,7 +135,7 @@ class TestEventHubBatchFunctionsStein(testutils.WebHostTestCase): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'eventhub_batch_functions' / \ + return testutils.EMULATOR_TESTS_FOLDER / 'eventhub_batch_functions' / \ 'eventhub_batch_functions_stein' @classmethod @@ -171,6 +176,9 @@ def test_eventhub_multiple(self): self.assertDictEqual(all_row_keys_seen, row_keys_seen) + @skipIf(sys.version_info.minor == 7, + "Using azure-eventhub SDK with the EventHub Emulator" + "requires Python 3.8+") @testutils.retryable_test(3, 5) def test_eventhub_multiple_with_metadata(self): # Generate a unique event body for EventHub event diff --git a/tests/endtoend/test_eventhub_functions.py b/tests/emulator_tests/test_eventhub_functions.py similarity index 90% rename from tests/endtoend/test_eventhub_functions.py rename to tests/emulator_tests/test_eventhub_functions.py index c4c3d74a..03088c73 100644 --- a/tests/endtoend/test_eventhub_functions.py +++ b/tests/emulator_tests/test_eventhub_functions.py @@ -1,8 +1,11 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. import json +import sys import time +from unittest import skipIf + from tests.utils import testutils @@ -17,7 +20,7 @@ class TestEventHubFunctions(testutils.WebHostTestCase): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'eventhub_functions' + return testutils.EMULATOR_TESTS_FOLDER / 'eventhub_functions' @classmethod def get_libraries_to_install(cls): @@ -52,6 +55,9 @@ def test_eventhub_trigger(self): # Check if the event body matches the initial data self.assertEqual(response, doc) + @skipIf(sys.version_info.minor == 7, + "Using azure-eventhub SDK with the EventHub Emulator" + "requires Python 3.8+") @testutils.retryable_test(3, 5) def test_eventhub_trigger_with_metadata(self): # Generate a unique event body for EventHub event @@ -106,13 +112,13 @@ class TestEventHubFunctionsStein(TestEventHubFunctions): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'eventhub_functions' / \ - 'eventhub_functions_stein' + return testutils.EMULATOR_TESTS_FOLDER / 'eventhub_functions' / \ + 'eventhub_functions_stein' class TestEventHubFunctionsSteinGeneric(TestEventHubFunctions): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'eventhub_functions' / \ + return testutils.EMULATOR_TESTS_FOLDER / 'eventhub_functions' / \ 'eventhub_functions_stein' / 'generic' diff --git a/tests/endtoend/test_generic_functions.py b/tests/emulator_tests/test_generic_functions.py similarity index 83% rename from tests/endtoend/test_generic_functions.py rename to tests/emulator_tests/test_generic_functions.py index 361b67f7..8dc44c83 100644 --- a/tests/endtoend/test_generic_functions.py +++ b/tests/emulator_tests/test_generic_functions.py @@ -17,12 +17,14 @@ class TestGenericFunctions(testutils.WebHostTestCase): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'generic_functions' + return testutils.EMULATOR_TESTS_FOLDER / 'generic_functions' def test_return_processed_last(self): # Tests the case where implicit and explicit return are true # in the same function and $return is processed before # the generic binding is + out_resp = self.webhost.request('POST', 'table_out_binding') + self.assertEqual(out_resp.status_code, 200) r = self.webhost.request('GET', 'return_processed_last') self.assertEqual(r.status_code, 200) @@ -31,11 +33,15 @@ def test_return_not_processed_last(self): # Tests the case where implicit and explicit return are true # in the same function and the generic binding is processed # before $return + out_resp = self.webhost.request('POST', 'table_out_binding') + self.assertEqual(out_resp.status_code, 200) r = self.webhost.request('GET', 'return_not_processed_last') self.assertEqual(r.status_code, 200) def test_return_types(self): + out_resp = self.webhost.request('POST', 'table_out_binding') + self.assertEqual(out_resp.status_code, 200) # Checking that the function app is okay time.sleep(10) # Checking webhost status. @@ -68,5 +74,5 @@ class TestGenericFunctionsStein(TestGenericFunctions): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'generic_functions' / \ + return testutils.EMULATOR_TESTS_FOLDER / 'generic_functions' / \ 'generic_functions_stein' diff --git a/tests/endtoend/test_queue_functions.py b/tests/emulator_tests/test_queue_functions.py similarity index 92% rename from tests/endtoend/test_queue_functions.py rename to tests/emulator_tests/test_queue_functions.py index 015e53ae..79362816 100644 --- a/tests/endtoend/test_queue_functions.py +++ b/tests/emulator_tests/test_queue_functions.py @@ -9,7 +9,7 @@ class TestQueueFunctions(testutils.WebHostTestCase): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'queue_functions' + return testutils.EMULATOR_TESTS_FOLDER / 'queue_functions' def test_queue_basic(self): r = self.webhost.request('POST', 'put_queue', @@ -91,13 +91,13 @@ class TestQueueFunctionsStein(TestQueueFunctions): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'queue_functions' / \ - 'queue_functions_stein' + return testutils.EMULATOR_TESTS_FOLDER / 'queue_functions' / \ + 'queue_functions_stein' class TestQueueFunctionsSteinGeneric(TestQueueFunctions): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'queue_functions' / \ + return testutils.EMULATOR_TESTS_FOLDER / 'queue_functions' / \ 'queue_functions_stein' / 'generic' diff --git a/tests/endtoend/test_table_functions.py b/tests/emulator_tests/test_table_functions.py similarity index 84% rename from tests/endtoend/test_table_functions.py rename to tests/emulator_tests/test_table_functions.py index d6e367bd..b5282bd9 100644 --- a/tests/endtoend/test_table_functions.py +++ b/tests/emulator_tests/test_table_functions.py @@ -11,7 +11,7 @@ class TestTableFunctions(testutils.WebHostTestCase): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'table_functions' + return testutils.EMULATOR_TESTS_FOLDER / 'table_functions' def test_table_bindings(self): out_resp = self.webhost.request('POST', 'table_out_binding') @@ -46,8 +46,8 @@ class TestTableFunctionsStein(testutils.WebHostTestCase): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'table_functions' / \ - 'table_functions_stein' + return testutils.EMULATOR_TESTS_FOLDER / 'table_functions' / \ + 'table_functions_stein' def test_table_bindings(self): out_resp = self.webhost.request('POST', 'table_out_binding') @@ -68,6 +68,6 @@ class TestTableFunctionsGeneric(TestTableFunctionsStein): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'table_functions' / \ - 'table_functions_stein' /\ - 'generic' + return testutils.EMULATOR_TESTS_FOLDER / 'table_functions' / \ + 'table_functions_stein' / \ + 'generic' diff --git a/tests/emulator_tests/utils/Config.json b/tests/emulator_tests/utils/Config.json new file mode 100644 index 00000000..710935c1 --- /dev/null +++ b/tests/emulator_tests/utils/Config.json @@ -0,0 +1,51 @@ +{ + "UserConfig": { + "NamespaceConfig": [ + { + "Type": "EventHub", + "Name": "emulatorNs1", + "Entities": [ + { + "Name": "python-worker-ci-eventhub-batch", + "PartitionCount": 2, + "ConsumerGroups": [ + { + "Name": "cg1" + } + ] + }, + { + "Name": "python-worker-ci-eventhub-batch-metadata", + "PartitionCount": 2, + "ConsumerGroups": [ + { + "Name": "cg1" + } + ] + }, + { + "Name": "python-worker-ci-eventhub-one", + "PartitionCount": 2, + "ConsumerGroups": [ + { + "Name": "cg1" + } + ] + }, + { + "Name": "python-worker-ci-eventhub-one-metadata", + "PartitionCount": 2, + "ConsumerGroups": [ + { + "Name": "cg1" + } + ] + } + ] + } + ], + "LoggingConfig": { + "Type": "File" + } + } +} \ No newline at end of file diff --git a/tests/emulator_tests/utils/docker-compose.yml b/tests/emulator_tests/utils/docker-compose.yml new file mode 100644 index 00000000..71b4ba38 --- /dev/null +++ b/tests/emulator_tests/utils/docker-compose.yml @@ -0,0 +1,34 @@ +name: microsoft-azure-eventhubs +services: + # Service for the Event Hubs Emulator + emulator: + container_name: "eventhubs-emulator" + image: "mcr.microsoft.com/azure-messaging/eventhubs-emulator:latest" + volumes: + - "./Config.json:/Eventhubs_Emulator/ConfigFiles/Config.json" + ports: + - "5672:5672" + environment: + BLOB_SERVER: azurite + METADATA_SERVER: azurite + ACCEPT_EULA: Y + depends_on: + - azurite + networks: + eh-emulator: + aliases: + - "eventhubs-emulator" + # Service for the Azurite Storage Emulator + azurite: + container_name: "azurite" + image: "mcr.microsoft.com/azure-storage/azurite:latest" + ports: + - "10000:10000" + - "10001:10001" + - "10002:10002" + networks: + eh-emulator: + aliases: + - "azurite" +networks: + eh-emulator: \ No newline at end of file diff --git a/tests/unittests/test_http_functions_v2.py b/tests/unittests/test_http_functions_v2.py index 17af822c..157fa0aa 100644 --- a/tests/unittests/test_http_functions_v2.py +++ b/tests/unittests/test_http_functions_v2.py @@ -204,6 +204,7 @@ def test_accept_json(self): self.assertEqual(r_json, {'a': 'abc', 'd': 42}) self.assertEqual(r.headers['content-type'], 'application/json') + @testutils.retryable_test(3, 5) def test_unhandled_error(self): r = self.webhost.request('GET', 'unhandled_error') self.assertEqual(r.status_code, 500) @@ -331,6 +332,7 @@ def check_log_import_module_troubleshooting_url(self, passed = True self.assertTrue(passed) + @testutils.retryable_test(3, 5) def test_print_logging_no_flush(self): r = self.webhost.request('GET', 'print_logging?message=Secret42') self.assertEqual(r.status_code, 200) diff --git a/tests/unittests/test_mock_blob_shared_memory_functions.py b/tests/unittests/test_mock_blob_shared_memory_functions.py index cdbbfc92..6c78561f 100644 --- a/tests/unittests/test_mock_blob_shared_memory_functions.py +++ b/tests/unittests/test_mock_blob_shared_memory_functions.py @@ -27,7 +27,7 @@ class TestMockBlobSharedMemoryFunctions(testutils.SharedMemoryTestCase, """ def setUp(self): super().setUp() - self.blob_funcs_dir = testutils.E2E_TESTS_FOLDER / 'blob_functions' + self.blob_funcs_dir = testutils.EMULATOR_TESTS_FOLDER / 'blob_functions' async def test_binary_blob_read_as_bytes_function(self): """ diff --git a/tests/utils/testutils.py b/tests/utils/testutils.py index aaeb40de..c04b134c 100644 --- a/tests/utils/testutils.py +++ b/tests/utils/testutils.py @@ -68,6 +68,7 @@ E2E_TESTS_ROOT = TESTS_ROOT / E2E_TESTS_FOLDER UNIT_TESTS_FOLDER = pathlib.Path('unittests') UNIT_TESTS_ROOT = TESTS_ROOT / UNIT_TESTS_FOLDER +EMULATOR_TESTS_FOLDER = pathlib.Path('emulator_tests') EXTENSION_TESTS_FOLDER = pathlib.Path('extension_tests') WEBHOST_DLL = "Microsoft.Azure.WebJobs.Script.WebHost.dll" DEFAULT_WEBHOST_DLL_PATH = (