diff --git a/CODEOWNERS b/CODEOWNERS index f93501102..e5f28aee3 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/emulator-tests.yml b/eng/ci/emulator-tests.yml new file mode 100644 index 000000000..adb1016f3 --- /dev/null +++ b/eng/ci/emulator-tests.yml @@ -0,0 +1,46 @@ +trigger: none # ensure this is not ran as a CI build + +pr: + branches: + include: + - dev + - release/* + +schedules: + - cron: "0 8 * * 1,2,3,4,5" + displayName: Monday to Friday 3 AM CST build + branches: + include: + - dev + always: true + +resources: + repositories: + - repository: 1es + type: git + name: 1ESPipelineTemplates/1ESPipelineTemplates + ref: refs/tags/release + - repository: eng + type: git + name: engineering + ref: refs/tags/release + +variables: + - template: /ci/variables/build.yml@eng + - template: /ci/variables/cfs.yml@eng + - template: /eng/templates/utils/variables.yml@self + +extends: + template: v1/1ES.Unofficial.PipelineTemplate.yml@1es + parameters: + pool: + name: 1es-pool-azfunc + image: 1es-windows-2022 + os: windows + + stages: + - stage: RunEmulatorTests + jobs: + - template: /eng/templates/jobs/ci-emulator-tests.yml@self + parameters: + PoolName: 1es-pool-azfunc \ No newline at end of file diff --git a/eng/ci/official-build.yml b/eng/ci/official-build.yml index ad332eb27..568fdf16b 100644 --- a/eng/ci/official-build.yml +++ b/eng/ci/official-build.yml @@ -54,6 +54,12 @@ extends: dependsOn: Build jobs: - template: /eng/templates/official/jobs/ci-e2e-tests.yml@self + - stage: RunEmulatorTests + dependsOn: Build + jobs: + - template: /eng/templates/jobs/ci-emulator-tests.yml@self + parameters: + PoolName: 1es-pool-azfunc - stage: RunUnitTests dependsOn: Build jobs: diff --git a/eng/ci/public-build.yml b/eng/ci/public-build.yml index 222cdc2dc..26f1b6625 100644 --- a/eng/ci/public-build.yml +++ b/eng/ci/public-build.yml @@ -53,4 +53,10 @@ 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 + parameters: + PoolName: 1es-pool-azfunc-public \ 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 000000000..d2ab3ce87 --- /dev/null +++ b/eng/templates/jobs/ci-emulator-tests.yml @@ -0,0 +1,100 @@ +jobs: + - job: "TestPython" + displayName: "Run Python Emulator Tests" + + pool: + name: ${{ parameters.PoolName }} + 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/eventhub/docker-compose.yml pull + docker compose -f tests/emulator_tests/utils/eventhub/docker-compose.yml up -d + displayName: 'Install Azurite and Start EventHub Emulator' + - bash: | + python -m pytest -q -n auto --dist loadfile --reruns 4 --ignore=tests/emulator_tests/test_servicebus_functions.py tests/emulator_tests + env: + AzureWebJobsStorage: "UseDevelopmentStorage=true" + AzureWebJobsEventHubConnectionString: "Endpoint=sb://localhost;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;" + displayName: "Running $(PYTHON_VERSION) Python Linux Emulator Tests" + - bash: | + # Stop and remove EventHub Emulator container to free up the port + docker stop eventhubs-emulator + docker container rm --force eventhubs-emulator + docker compose -f tests/emulator_tests/utils/servicebus/docker-compose.yml pull + docker compose -f tests/emulator_tests/utils/servicebus/docker-compose.yml up -d + env: + AzureWebJobsSQLPassword: $(AzureWebJobsSQLPassword) + displayName: 'Install Azurite and Start ServiceBus Emulator' + - bash: | + python -m pytest -q -n auto --dist loadfile --reruns 4 tests/emulator_tests/test_servicebus_functions.py + env: + AzureWebJobsStorage: "UseDevelopmentStorage=true" + AzureWebJobsServiceBusConnectionString: "Endpoint=sb://localhost;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;" + displayName: "Running $(PYTHON_VERSION) Python ServiceBus Linux 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 1b77b8589..24489b0e6 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,445 +1,445 @@ -# 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_sha256 = hashlib.sha256(file).hexdigest() - - response_dict = { - 'content_size': content_size, - 'content_sha256': content_sha256 - } - - 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_sha256 = hashlib.sha256(file_bytes).hexdigest() - - response_dict = { - 'content_size': content_size, - 'content_sha256': content_sha256 - } - - 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_sha256 = hashlib.sha256(content_bytes).hexdigest() - - response_dict = { - 'num_chars': num_chars, - 'content_sha256': content_sha256 - } - - 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_sha256 = hashlib.sha256(content).hexdigest() - - file.set(content) - - response_dict = { - 'content_size': content_size, - 'content_sha256': content_sha256 - } - - 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_sha256 = hashlib.sha256(content_bytes).hexdigest() - - file.set(content) - - response_dict = { - 'num_chars': num_chars, - 'content_size': content_size, - 'content_sha256': content_sha256 - } - - 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_sha256 = hashlib.sha256(content).hexdigest() - return content, content_sha256 - - -@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_sha256_1 = hashlib.sha256(inputfile1).hexdigest() - input_content_sha256_2 = hashlib.sha256(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_sha256_1 = \ - _generate_content_and_digest(output_content_size_1) - output_content_2, output_content_sha256_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_sha256_1': input_content_sha256_1, - 'input_content_sha256_2': input_content_sha256_2, - 'output_content_size_1': output_content_size_1, - 'output_content_size_2': output_content_size_2, - 'output_content_sha256_1': output_content_sha256_1, - 'output_content_sha256_2': output_content_sha256_2 - } - - response_body = json.dumps(response_dict, indent=2) - - return func.HttpResponse( - body=response_body, - mimetype="application/json", - status_code=200 - ) - - -@app.function_name(name="blob_trigger_default_source_enum") -@app.blob_trigger(arg_name="file", - path="python-worker-tests/test-blob-trigger.txt", - connection="AzureWebJobsStorage", - source=func.BlobSource.LOGS_AND_CONTAINER_SCAN) -def blob_trigger_default_source_enum(file: func.InputStream) -> str: - return json.dumps({ - 'name': file.name, - 'length': file.length, - 'content': file.read().decode('utf-8') - }) - - -@app.function_name(name="blob_trigger_eventgrid_source_enum") -@app.blob_trigger(arg_name="file", - path="python-worker-tests/test-blob-trigger.txt", - connection="AzureWebJobsStorage", - source=func.BlobSource.EVENT_GRID) -def blob_trigger_eventgrid_source_enum(file: func.InputStream) -> str: - return json.dumps({ - 'name': file.name, - 'length': file.length, - 'content': file.read().decode('utf-8') - }) - - -@app.function_name(name="blob_trigger_default_source_str") -@app.blob_trigger(arg_name="file", - path="python-worker-tests/test-blob-trigger.txt", - connection="AzureWebJobsStorage", - source="LogsAndContainerScan") -def blob_trigger_default_source_str(file: func.InputStream) -> str: - return json.dumps({ - 'name': file.name, - 'length': file.length, - 'content': file.read().decode('utf-8') - }) - - -@app.function_name(name="blob_trigger_eventgrid_source_str") -@app.blob_trigger(arg_name="file", - path="python-worker-tests/test-blob-trigger.txt", - connection="AzureWebJobsStorage", - source="EventGrid") -def blob_trigger_eventgrid_source_str(file: func.InputStream) -> str: - return json.dumps({ - 'name': file.name, - 'length': file.length, - 'content': file.read().decode('utf-8') - }) +# 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_sha256 = hashlib.sha256(file).hexdigest() + + response_dict = { + 'content_size': content_size, + 'content_sha256': content_sha256 + } + + 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_sha256 = hashlib.sha256(file_bytes).hexdigest() + + response_dict = { + 'content_size': content_size, + 'content_sha256': content_sha256 + } + + 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_sha256 = hashlib.sha256(content_bytes).hexdigest() + + response_dict = { + 'num_chars': num_chars, + 'content_sha256': content_sha256 + } + + 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_sha256 = hashlib.sha256(content).hexdigest() + + file.set(content) + + response_dict = { + 'content_size': content_size, + 'content_sha256': content_sha256 + } + + 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_sha256 = hashlib.sha256(content_bytes).hexdigest() + + file.set(content) + + response_dict = { + 'num_chars': num_chars, + 'content_size': content_size, + 'content_sha256': content_sha256 + } + + 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_sha256 = hashlib.sha256(content).hexdigest() + return content, content_sha256 + + +@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_sha256_1 = hashlib.sha256(inputfile1).hexdigest() + input_content_sha256_2 = hashlib.sha256(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_sha256_1 = \ + _generate_content_and_digest(output_content_size_1) + output_content_2, output_content_sha256_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_sha256_1': input_content_sha256_1, + 'input_content_sha256_2': input_content_sha256_2, + 'output_content_size_1': output_content_size_1, + 'output_content_size_2': output_content_size_2, + 'output_content_sha256_1': output_content_sha256_1, + 'output_content_sha256_2': output_content_sha256_2 + } + + response_body = json.dumps(response_dict, indent=2) + + return func.HttpResponse( + body=response_body, + mimetype="application/json", + status_code=200 + ) + + +@app.function_name(name="blob_trigger_default_source_enum") +@app.blob_trigger(arg_name="file", + path="python-worker-tests/test-blob-trigger.txt", + connection="AzureWebJobsStorage", + source=func.BlobSource.LOGS_AND_CONTAINER_SCAN) +def blob_trigger_default_source_enum(file: func.InputStream) -> str: + return json.dumps({ + 'name': file.name, + 'length': file.length, + 'content': file.read().decode('utf-8') + }) + + +@app.function_name(name="blob_trigger_eventgrid_source_enum") +@app.blob_trigger(arg_name="file", + path="python-worker-tests/test-blob-trigger.txt", + connection="AzureWebJobsStorage", + source=func.BlobSource.EVENT_GRID) +def blob_trigger_eventgrid_source_enum(file: func.InputStream) -> str: + return json.dumps({ + 'name': file.name, + 'length': file.length, + 'content': file.read().decode('utf-8') + }) + + +@app.function_name(name="blob_trigger_default_source_str") +@app.blob_trigger(arg_name="file", + path="python-worker-tests/test-blob-trigger.txt", + connection="AzureWebJobsStorage", + source="LogsAndContainerScan") +def blob_trigger_default_source_str(file: func.InputStream) -> str: + return json.dumps({ + 'name': file.name, + 'length': file.length, + 'content': file.read().decode('utf-8') + }) + + +@app.function_name(name="blob_trigger_eventgrid_source_str") +@app.blob_trigger(arg_name="file", + path="python-worker-tests/test-blob-trigger.txt", + connection="AzureWebJobsStorage", + source="EventGrid") +def blob_trigger_eventgrid_source_str(file: func.InputStream) -> str: + return json.dumps({ + 'name': file.name, + 'length': file.length, + 'content': file.read().decode('utf-8') + }) 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 3e8a6995d..8ec2e9d65 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 1091f8dd5..1481f7b55 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 654148c93..2da6d44ca 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 66d1e80e1..e02ae4d15 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 82ac266a6..d23f01a86 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 000000000..09c7058e9 --- /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 000000000..25537873a --- /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 0b883fb12..087cf4592 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/servicebus_functions/get_servicebus_triggered/__init__.py b/tests/emulator_tests/servicebus_functions/get_servicebus_triggered/__init__.py similarity index 100% rename from tests/endtoend/servicebus_functions/get_servicebus_triggered/__init__.py rename to tests/emulator_tests/servicebus_functions/get_servicebus_triggered/__init__.py diff --git a/tests/endtoend/servicebus_functions/get_servicebus_triggered/function.json b/tests/emulator_tests/servicebus_functions/get_servicebus_triggered/function.json similarity index 100% rename from tests/endtoend/servicebus_functions/get_servicebus_triggered/function.json rename to tests/emulator_tests/servicebus_functions/get_servicebus_triggered/function.json diff --git a/tests/endtoend/servicebus_functions/put_message/__init__.py b/tests/emulator_tests/servicebus_functions/put_message/__init__.py similarity index 100% rename from tests/endtoend/servicebus_functions/put_message/__init__.py rename to tests/emulator_tests/servicebus_functions/put_message/__init__.py diff --git a/tests/endtoend/servicebus_functions/put_message/function.json b/tests/emulator_tests/servicebus_functions/put_message/function.json similarity index 100% rename from tests/endtoend/servicebus_functions/put_message/function.json rename to tests/emulator_tests/servicebus_functions/put_message/function.json diff --git a/tests/endtoend/servicebus_functions/servicebus_functions_stein/function_app.py b/tests/emulator_tests/servicebus_functions/servicebus_functions_stein/function_app.py similarity index 97% rename from tests/endtoend/servicebus_functions/servicebus_functions_stein/function_app.py rename to tests/emulator_tests/servicebus_functions/servicebus_functions_stein/function_app.py index 4064085be..9e9d12246 100644 --- a/tests/endtoend/servicebus_functions/servicebus_functions_stein/function_app.py +++ b/tests/emulator_tests/servicebus_functions/servicebus_functions_stein/function_app.py @@ -1,73 +1,73 @@ -import json - -import azure.functions as func - -app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) - - -@app.route(route="put_message") -@app.service_bus_queue_output( - arg_name="msg", - connection="AzureWebJobsServiceBusConnectionString", - queue_name="testqueue") -def put_message(req: func.HttpRequest, msg: func.Out[str]): - msg.set(req.get_body().decode('utf-8')) - return 'OK' - - -@app.route(route="get_servicebus_triggered") -@app.blob_input(arg_name="file", - path="python-worker-tests/test-servicebus-triggered.txt", - connection="AzureWebJobsStorage") -def get_servicebus_triggered(req: func.HttpRequest, - file: func.InputStream) -> str: - return func.HttpResponse( - file.read().decode('utf-8'), mimetype='application/json') - - -@app.service_bus_queue_trigger( - arg_name="msg", - connection="AzureWebJobsServiceBusConnectionString", - queue_name="testqueue") -@app.blob_output(arg_name="$return", - path="python-worker-tests/test-servicebus-triggered.txt", - connection="AzureWebJobsStorage") -def servicebus_trigger(msg: func.ServiceBusMessage) -> str: - result = json.dumps({ - 'message_id': msg.message_id, - 'body': msg.get_body().decode('utf-8'), - 'content_type': msg.content_type, - 'delivery_count': msg.delivery_count, - 'expiration_time': (msg.expiration_time.isoformat() if - msg.expiration_time else None), - 'label': msg.label, - 'partition_key': msg.partition_key, - 'reply_to': msg.reply_to, - 'reply_to_session_id': msg.reply_to_session_id, - 'scheduled_enqueue_time': (msg.scheduled_enqueue_time.isoformat() if - msg.scheduled_enqueue_time else None), - 'session_id': msg.session_id, - 'time_to_live': msg.time_to_live, - 'to': msg.to, - 'user_properties': msg.user_properties, - - 'application_properties': msg.application_properties, - 'correlation_id': msg.correlation_id, - 'dead_letter_error_description': msg.dead_letter_error_description, - 'dead_letter_reason': msg.dead_letter_reason, - 'dead_letter_source': msg.dead_letter_source, - 'enqueued_sequence_number': msg.enqueued_sequence_number, - 'enqueued_time_utc': (msg.enqueued_time_utc.isoformat() if - msg.enqueued_time_utc else None), - 'expires_at_utc': (msg.expires_at_utc.isoformat() if - msg.expires_at_utc else None), - 'locked_until': (msg.locked_until.isoformat() if - msg.locked_until else None), - 'lock_token': msg.lock_token, - 'sequence_number': msg.sequence_number, - 'state': msg.state, - 'subject': msg.subject, - 'transaction_partition_key': msg.transaction_partition_key - }) - - return result +import json + +import azure.functions as func + +app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) + + +@app.route(route="put_message") +@app.service_bus_queue_output( + arg_name="msg", + connection="AzureWebJobsServiceBusConnectionString", + queue_name="testqueue") +def put_message(req: func.HttpRequest, msg: func.Out[str]): + msg.set(req.get_body().decode('utf-8')) + return 'OK' + + +@app.route(route="get_servicebus_triggered") +@app.blob_input(arg_name="file", + path="python-worker-tests/test-servicebus-triggered.txt", + connection="AzureWebJobsStorage") +def get_servicebus_triggered(req: func.HttpRequest, + file: func.InputStream) -> str: + return func.HttpResponse( + file.read().decode('utf-8'), mimetype='application/json') + + +@app.service_bus_queue_trigger( + arg_name="msg", + connection="AzureWebJobsServiceBusConnectionString", + queue_name="testqueue") +@app.blob_output(arg_name="$return", + path="python-worker-tests/test-servicebus-triggered.txt", + connection="AzureWebJobsStorage") +def servicebus_trigger(msg: func.ServiceBusMessage) -> str: + result = json.dumps({ + 'message_id': msg.message_id, + 'body': msg.get_body().decode('utf-8'), + 'content_type': msg.content_type, + 'delivery_count': msg.delivery_count, + 'expiration_time': (msg.expiration_time.isoformat() if + msg.expiration_time else None), + 'label': msg.label, + 'partition_key': msg.partition_key, + 'reply_to': msg.reply_to, + 'reply_to_session_id': msg.reply_to_session_id, + 'scheduled_enqueue_time': (msg.scheduled_enqueue_time.isoformat() if + msg.scheduled_enqueue_time else None), + 'session_id': msg.session_id, + 'time_to_live': msg.time_to_live, + 'to': msg.to, + 'user_properties': msg.user_properties, + + 'application_properties': msg.application_properties, + 'correlation_id': msg.correlation_id, + 'dead_letter_error_description': msg.dead_letter_error_description, + 'dead_letter_reason': msg.dead_letter_reason, + 'dead_letter_source': msg.dead_letter_source, + 'enqueued_sequence_number': msg.enqueued_sequence_number, + 'enqueued_time_utc': (msg.enqueued_time_utc.isoformat() if + msg.enqueued_time_utc else None), + 'expires_at_utc': (msg.expires_at_utc.isoformat() if + msg.expires_at_utc else None), + 'locked_until': (msg.locked_until.isoformat() if + msg.locked_until else None), + 'lock_token': msg.lock_token, + 'sequence_number': msg.sequence_number, + 'state': msg.state, + 'subject': msg.subject, + 'transaction_partition_key': msg.transaction_partition_key + }) + + return result diff --git a/tests/endtoend/servicebus_functions/servicebus_functions_stein/generic/function_app.py b/tests/emulator_tests/servicebus_functions/servicebus_functions_stein/generic/function_app.py similarity index 97% rename from tests/endtoend/servicebus_functions/servicebus_functions_stein/generic/function_app.py rename to tests/emulator_tests/servicebus_functions/servicebus_functions_stein/generic/function_app.py index 3b340153a..4fd48785a 100644 --- a/tests/endtoend/servicebus_functions/servicebus_functions_stein/generic/function_app.py +++ b/tests/emulator_tests/servicebus_functions/servicebus_functions_stein/generic/function_app.py @@ -1,81 +1,81 @@ -import json - -import azure.functions as func - -app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) - - -@app.function_name(name="put_message") -@app.generic_trigger(arg_name="req", type="httpTrigger", route="put_message") -@app.generic_output_binding(arg_name="msg", - type="serviceBus", - connection="AzureWebJobsServiceBusConnectionString", - queue_name="testqueue") -@app.generic_output_binding(arg_name="$return", type="http") -def put_message(req: func.HttpRequest, msg: func.Out[str]): - msg.set(req.get_body().decode('utf-8')) - return 'OK' - - -@app.function_name(name="get_servicebus_triggered") -@app.generic_trigger(arg_name="req", type="httpTrigger", - route="get_servicebus_triggered") -@app.generic_input_binding(arg_name="file", - type="blob", - path="python-worker-tests/test-servicebus-triggered.txt", # NoQA - connection="AzureWebJobsStorage") -@app.generic_output_binding(arg_name="$return", type="http") -def get_servicebus_triggered(req: func.HttpRequest, - file: func.InputStream) -> str: - return func.HttpResponse( - file.read().decode('utf-8'), mimetype='application/json') - - -@app.generic_trigger( - arg_name="msg", - type="serviceBusTrigger", - connection="AzureWebJobsServiceBusConnectionString", - queue_name="testqueue") -@app.generic_output_binding(arg_name="$return", - path="python-worker-tests/test-servicebus-triggered.txt", # NoQA - type="blob", - connection="AzureWebJobsStorage") -def servicebus_trigger(msg: func.ServiceBusMessage) -> str: - result = json.dumps({ - 'message_id': msg.message_id, - 'body': msg.get_body().decode('utf-8'), - 'content_type': msg.content_type, - 'delivery_count': msg.delivery_count, - 'expiration_time': (msg.expiration_time.isoformat() if - msg.expiration_time else None), - 'label': msg.label, - 'partition_key': msg.partition_key, - 'reply_to': msg.reply_to, - 'reply_to_session_id': msg.reply_to_session_id, - 'scheduled_enqueue_time': (msg.scheduled_enqueue_time.isoformat() if - msg.scheduled_enqueue_time else None), - 'session_id': msg.session_id, - 'time_to_live': msg.time_to_live, - 'to': msg.to, - 'user_properties': msg.user_properties, - - 'application_properties': msg.application_properties, - 'correlation_id': msg.correlation_id, - 'dead_letter_error_description': msg.dead_letter_error_description, - 'dead_letter_reason': msg.dead_letter_reason, - 'dead_letter_source': msg.dead_letter_source, - 'enqueued_sequence_number': msg.enqueued_sequence_number, - 'enqueued_time_utc': (msg.enqueued_time_utc.isoformat() if - msg.enqueued_time_utc else None), - 'expires_at_utc': (msg.expires_at_utc.isoformat() if - msg.expires_at_utc else None), - 'locked_until': (msg.locked_until.isoformat() if - msg.locked_until else None), - 'lock_token': msg.lock_token, - 'sequence_number': msg.sequence_number, - 'state': msg.state, - 'subject': msg.subject, - 'transaction_partition_key': msg.transaction_partition_key - }) - - return result +import json + +import azure.functions as func + +app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS) + + +@app.function_name(name="put_message") +@app.generic_trigger(arg_name="req", type="httpTrigger", route="put_message") +@app.generic_output_binding(arg_name="msg", + type="serviceBus", + connection="AzureWebJobsServiceBusConnectionString", + queue_name="testqueue") +@app.generic_output_binding(arg_name="$return", type="http") +def put_message(req: func.HttpRequest, msg: func.Out[str]): + msg.set(req.get_body().decode('utf-8')) + return 'OK' + + +@app.function_name(name="get_servicebus_triggered") +@app.generic_trigger(arg_name="req", type="httpTrigger", + route="get_servicebus_triggered") +@app.generic_input_binding(arg_name="file", + type="blob", + path="python-worker-tests/test-servicebus-triggered.txt", # NoQA + connection="AzureWebJobsStorage") +@app.generic_output_binding(arg_name="$return", type="http") +def get_servicebus_triggered(req: func.HttpRequest, + file: func.InputStream) -> str: + return func.HttpResponse( + file.read().decode('utf-8'), mimetype='application/json') + + +@app.generic_trigger( + arg_name="msg", + type="serviceBusTrigger", + connection="AzureWebJobsServiceBusConnectionString", + queue_name="testqueue") +@app.generic_output_binding(arg_name="$return", + path="python-worker-tests/test-servicebus-triggered.txt", # NoQA + type="blob", + connection="AzureWebJobsStorage") +def servicebus_trigger(msg: func.ServiceBusMessage) -> str: + result = json.dumps({ + 'message_id': msg.message_id, + 'body': msg.get_body().decode('utf-8'), + 'content_type': msg.content_type, + 'delivery_count': msg.delivery_count, + 'expiration_time': (msg.expiration_time.isoformat() if + msg.expiration_time else None), + 'label': msg.label, + 'partition_key': msg.partition_key, + 'reply_to': msg.reply_to, + 'reply_to_session_id': msg.reply_to_session_id, + 'scheduled_enqueue_time': (msg.scheduled_enqueue_time.isoformat() if + msg.scheduled_enqueue_time else None), + 'session_id': msg.session_id, + 'time_to_live': msg.time_to_live, + 'to': msg.to, + 'user_properties': msg.user_properties, + + 'application_properties': msg.application_properties, + 'correlation_id': msg.correlation_id, + 'dead_letter_error_description': msg.dead_letter_error_description, + 'dead_letter_reason': msg.dead_letter_reason, + 'dead_letter_source': msg.dead_letter_source, + 'enqueued_sequence_number': msg.enqueued_sequence_number, + 'enqueued_time_utc': (msg.enqueued_time_utc.isoformat() if + msg.enqueued_time_utc else None), + 'expires_at_utc': (msg.expires_at_utc.isoformat() if + msg.expires_at_utc else None), + 'locked_until': (msg.locked_until.isoformat() if + msg.locked_until else None), + 'lock_token': msg.lock_token, + 'sequence_number': msg.sequence_number, + 'state': msg.state, + 'subject': msg.subject, + 'transaction_partition_key': msg.transaction_partition_key + }) + + return result diff --git a/tests/endtoend/servicebus_functions/servicebus_trigger/__init__.py b/tests/emulator_tests/servicebus_functions/servicebus_trigger/__init__.py similarity index 100% rename from tests/endtoend/servicebus_functions/servicebus_trigger/__init__.py rename to tests/emulator_tests/servicebus_functions/servicebus_trigger/__init__.py diff --git a/tests/endtoend/servicebus_functions/servicebus_trigger/function.json b/tests/emulator_tests/servicebus_functions/servicebus_trigger/function.json similarity index 100% rename from tests/endtoend/servicebus_functions/servicebus_trigger/function.json rename to tests/emulator_tests/servicebus_functions/servicebus_trigger/function.json 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 4d51693e9..d6a840a38 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 11cf4d389..1a8ae2a9e 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 c4c3d74ab..03088c731 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 361b67f7c..8dc44c835 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 015e53ae4..793628169 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_servicebus_functions.py b/tests/emulator_tests/test_servicebus_functions.py similarity index 84% rename from tests/endtoend/test_servicebus_functions.py rename to tests/emulator_tests/test_servicebus_functions.py index aaacd76d6..2e6bd7310 100644 --- a/tests/endtoend/test_servicebus_functions.py +++ b/tests/emulator_tests/test_servicebus_functions.py @@ -10,7 +10,7 @@ class TestServiceBusFunctions(testutils.WebHostTestCase): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'servicebus_functions' + return testutils.EMULATOR_TESTS_FOLDER / 'servicebus_functions' @testutils.retryable_test(3, 5) def test_servicebus_basic(self): @@ -53,14 +53,13 @@ class TestServiceBusFunctionsStein(TestServiceBusFunctions): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'servicebus_functions' / \ - 'servicebus_functions_stein' + return testutils.EMULATOR_TESTS_FOLDER / 'servicebus_functions' / \ + 'servicebus_functions_stein' class TestServiceBusFunctionsSteinGeneric(TestServiceBusFunctions): @classmethod def get_script_dir(cls): - return testutils.E2E_TESTS_FOLDER / 'servicebus_functions' / \ - 'servicebus_functions_stein' / \ - 'generic' + return testutils.EMULATOR_TESTS_FOLDER / 'servicebus_functions' / \ + 'servicebus_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 d6e367bdc..b5282bd92 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/eventhub/config.json b/tests/emulator_tests/utils/eventhub/config.json new file mode 100644 index 000000000..710935c14 --- /dev/null +++ b/tests/emulator_tests/utils/eventhub/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/eventhub/docker-compose.yml b/tests/emulator_tests/utils/eventhub/docker-compose.yml new file mode 100644 index 000000000..2c40aa042 --- /dev/null +++ b/tests/emulator_tests/utils/eventhub/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/emulator_tests/utils/servicebus/config.json b/tests/emulator_tests/utils/servicebus/config.json new file mode 100644 index 000000000..20cf83447 --- /dev/null +++ b/tests/emulator_tests/utils/servicebus/config.json @@ -0,0 +1,28 @@ +{ + "UserConfig": { + "Namespaces": [ + { + "Name": "sbemulatorns", + "Queues": [ + { + "Name": "testqueue", + "Properties": { + "DeadLetteringOnMessageExpiration": false, + "DefaultMessageTimeToLive": "PT1H", + "DuplicateDetectionHistoryTimeWindow": "PT20S", + "ForwardDeadLetteredMessagesTo": "", + "ForwardTo": "", + "LockDuration": "PT1M", + "MaxDeliveryCount": 10, + "RequiresDuplicateDetection": false, + "RequiresSession": false + } + } + ] + } + ], + "Logging": { + "Type": "File" + } + } +} \ No newline at end of file diff --git a/tests/emulator_tests/utils/servicebus/docker-compose.yml b/tests/emulator_tests/utils/servicebus/docker-compose.yml new file mode 100644 index 000000000..c1781a858 --- /dev/null +++ b/tests/emulator_tests/utils/servicebus/docker-compose.yml @@ -0,0 +1,40 @@ +name: microsoft-azure-servicebus +services: + # Service for the Service Bus Emulator + sbemulator: + container_name: "servicebus-emulator" + image: mcr.microsoft.com/azure-messaging/servicebus-emulator:latest + volumes: + - "./config.json:/ServiceBus_Emulator/ConfigFiles/Config.json" + ports: + - "5672:5672" + environment: + SQL_SERVER: sqledge + MSSQL_SA_PASSWORD: ${AzureWebJobsSQLPassword} + ACCEPT_EULA: Y + depends_on: + - sqledge + networks: + sb-emulator: + aliases: + - "sb-emulator" + sqledge: + container_name: "sqledge" + image: "mcr.microsoft.com/azure-sql-edge:latest" + networks: + sb-emulator: + aliases: + - "sqledge" + environment: + ACCEPT_EULA: Y + MSSQL_SA_PASSWORD: ${AzureWebJobsSQLPassword} + # Service for the Azurite Storage Emulator + azurite: + container_name: "azurite-sb" + image: "mcr.microsoft.com/azure-storage/azurite:latest" + ports: + - "10003:10003" + - "10004:10004" + - "10005:10005" +networks: + sb-emulator: \ No newline at end of file diff --git a/tests/endtoend/test_worker_process_count_functions.py b/tests/endtoend/test_worker_process_count_functions.py index 8ee6577bc..44abcd2e2 100644 --- a/tests/endtoend/test_worker_process_count_functions.py +++ b/tests/endtoend/test_worker_process_count_functions.py @@ -1,12 +1,19 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. import os + from datetime import datetime from threading import Thread +from unittest import skipIf from tests.utils import testutils +from azure_functions_worker.utils.common import is_envvar_true +from tests.utils.constants import CONSUMPTION_DOCKER_TEST, DEDICATED_DOCKER_TEST +@skipIf(is_envvar_true(DEDICATED_DOCKER_TEST) + or is_envvar_true(CONSUMPTION_DOCKER_TEST), + "Tests are flaky when running on Docker") class TestWorkerProcessCount(testutils.WebHostTestCase): """Test the Http Trigger with setting up the python worker process count to 2. this test will check if both requests should be processed at the @@ -63,24 +70,30 @@ def http_req(res_num): self.assertTrue(time_diff_in_seconds < 1) +@skipIf(is_envvar_true(DEDICATED_DOCKER_TEST) + or is_envvar_true(CONSUMPTION_DOCKER_TEST), + "Tests are flaky when running on Docker") class TestWorkerProcessCountStein(TestWorkerProcessCount): - @classmethod def get_script_dir(cls): return testutils.E2E_TESTS_FOLDER / 'http_functions' /\ 'http_functions_stein' +@skipIf(is_envvar_true(DEDICATED_DOCKER_TEST) + or is_envvar_true(CONSUMPTION_DOCKER_TEST), + "Tests are flaky when running on Docker") class TestWorkerProcessCountWithBlueprintStein(TestWorkerProcessCount): - @classmethod def get_script_dir(cls): return testutils.E2E_TESTS_FOLDER / 'blueprint_functions' /\ 'functions_in_blueprint_only' +@skipIf(is_envvar_true(DEDICATED_DOCKER_TEST) + or is_envvar_true(CONSUMPTION_DOCKER_TEST), + "Tests are flaky when running on Docker") class TestWorkerProcessCountWithBlueprintDiffDirStein(TestWorkerProcessCount): - @classmethod def get_script_dir(cls): return testutils.E2E_TESTS_FOLDER / 'blueprint_functions' /\ diff --git a/tests/extension_tests/http_v2_tests/test_http_v2.py b/tests/extension_tests/http_v2_tests/test_http_v2.py index 8409b7e58..8c1d5b48e 100644 --- a/tests/extension_tests/http_v2_tests/test_http_v2.py +++ b/tests/extension_tests/http_v2_tests/test_http_v2.py @@ -8,12 +8,17 @@ import requests from tests.utils import testutils +from azure_functions_worker.utils.common import is_envvar_true +from tests.utils.constants import CONSUMPTION_DOCKER_TEST, DEDICATED_DOCKER_TEST from azure_functions_worker.constants import PYTHON_ENABLE_INIT_INDEXING REQUEST_TIMEOUT_SEC = 5 +@unittest.skipIf(is_envvar_true(DEDICATED_DOCKER_TEST) + or is_envvar_true(CONSUMPTION_DOCKER_TEST), + "Tests are flaky when running on Docker") @unittest.skipIf(sys.version_info.minor < 8, "HTTPv2" "is only supported for 3.8+.") class TestHttpFunctionsWithInitIndexing(testutils.WebHostTestCase): diff --git a/tests/unittests/test_http_functions_v2.py b/tests/unittests/test_http_functions_v2.py index 6aebad6a0..b7e456671 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 f11545422..63b06ca12 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 aaeb40dec..c04b134c5 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 = (