From 8b21929b899b5a2695e2c3a59772822829cbaf15 Mon Sep 17 00:00:00 2001 From: luczhan Date: Tue, 10 May 2022 11:59:54 -0700 Subject: [PATCH 1/8] add sql bindings e2e test --- .ci/e2e_integration_test/pipeline.yml | 1 + .ci/linux_devops_e2e_tests.sh | 1 + .github/workflows/ci_e2e_workflow.yml | 4 ++ azure_functions_worker/testutils.py | 4 ++ .../sql_functions/sql_input/__init__.py | 14 +++++++ .../sql_functions/sql_input/function.json | 26 +++++++++++++ .../sql_functions/sql_output/__init__.py | 21 ++++++++++ .../sql_functions/sql_output/function.json | 28 +++++++++++++ tests/endtoend/test_sql_functions.py | 39 +++++++++++++++++++ 9 files changed, 138 insertions(+) create mode 100644 tests/endtoend/sql_functions/sql_input/__init__.py create mode 100644 tests/endtoend/sql_functions/sql_input/function.json create mode 100644 tests/endtoend/sql_functions/sql_output/__init__.py create mode 100644 tests/endtoend/sql_functions/sql_output/function.json create mode 100644 tests/endtoend/test_sql_functions.py diff --git a/.ci/e2e_integration_test/pipeline.yml b/.ci/e2e_integration_test/pipeline.yml index 3c77eeab..33a56702 100644 --- a/.ci/e2e_integration_test/pipeline.yml +++ b/.ci/e2e_integration_test/pipeline.yml @@ -36,6 +36,7 @@ steps: AzureWebJobsCosmosDBConnectionString: $(AzureWebJobsCosmosDBConnectionString) AzureWebJobsEventHubConnectionString: $(AzureWebJobsEventHubConnectionString) AzureWebJobsServiceBusConnectionString: $(AzureWebJobsServiceBusConnectionString) + AzureWebJobsSqlConnectionString: $(AzureWebJobsSqlConnectionString) AzureWebJobsEventGridTopicUri: $(AzureWebJobsEventGridTopicUri) AzureWebJobsEventGridConnectionKey: $(AzureWebJobsEventGridConnectionKey) PythonVersion: $(PYTHON_VERSION) diff --git a/.ci/linux_devops_e2e_tests.sh b/.ci/linux_devops_e2e_tests.sh index 83939d38..7a5d5105 100644 --- a/.ci/linux_devops_e2e_tests.sh +++ b/.ci/linux_devops_e2e_tests.sh @@ -5,6 +5,7 @@ export AzureWebJobsStorage=$LINUXSTORAGECONNECTIONSTRING export AzureWebJobsCosmosDBConnectionString=$LINUXCOSMOSDBCONNECTIONSTRING export AzureWebJobsEventHubConnectionString=$LINUXEVENTHUBCONNECTIONSTRING export AzureWebJobsServiceBusConnectionString=$LINUXSERVICEBUSCONNECTIONSTRING +export AzureWebJobsSqlConnectionString=$LINUXSQLCONNECTIONSTRING export AzureWebJobsEventGridTopicUri=$LINUXEVENTGRIDTOPICURI export AzureWebJobsEventGridConnectionKey=$LINUXEVENTGRIDTOPICCONNECTIONKEY diff --git a/.github/workflows/ci_e2e_workflow.yml b/.github/workflows/ci_e2e_workflow.yml index f0aad363..85165266 100644 --- a/.github/workflows/ci_e2e_workflow.yml +++ b/.github/workflows/ci_e2e_workflow.yml @@ -76,6 +76,7 @@ jobs: AzureWebJobsCosmosDBConnectionString: ${{ secrets.LinuxCosmosDBConnectionString36 }} AzureWebJobsEventHubConnectionString: ${{ secrets.LinuxEventHubConnectionString36 }} AzureWebJobsServiceBusConnectionString: ${{ secrets.LinuxServiceBusConnectionString36 }} + AzureWebJobsSqlConnectionString: ${{ secrets.LinuxSqlConnectionString36 }} AzureWebJobsEventGridTopicUri: ${{ secrets.LinuxEventGridTopicUriString36 }} AzureWebJobsEventGridConnectionKey: ${{ secrets.LinuxEventGridConnectionKeyString36 }} run: | @@ -87,6 +88,7 @@ jobs: AzureWebJobsCosmosDBConnectionString: ${{ secrets.LinuxCosmosDBConnectionString37 }} AzureWebJobsEventHubConnectionString: ${{ secrets.LinuxEventHubConnectionString37 }} AzureWebJobsServiceBusConnectionString: ${{ secrets.LinuxServiceBusConnectionString37 }} + AzureWebJobsSqlConnectionString: ${{ secrets.LinuxSqlConnectionString37 }} AzureWebJobsEventGridTopicUri: ${{ secrets.LinuxEventGridTopicUriString37 }} AzureWebJobsEventGridConnectionKey: ${{ secrets.LinuxEventGridConnectionKeyString37 }} run: | @@ -98,6 +100,7 @@ jobs: AzureWebJobsCosmosDBConnectionString: ${{ secrets.LinuxCosmosDBConnectionString38 }} AzureWebJobsEventHubConnectionString: ${{ secrets.LinuxEventHubConnectionString38 }} AzureWebJobsServiceBusConnectionString: ${{ secrets.LinuxServiceBusConnectionString38 }} + AzureWebJobsSqlConnectionString: ${{ secrets.LinuxSqlConnectionString38 }} AzureWebJobsEventGridTopicUri: ${{ secrets.LinuxEventGridTopicUriString38 }} AzureWebJobsEventGridConnectionKey: ${{ secrets.LinuxEventGridConnectionKeyString38 }} run: | @@ -109,6 +112,7 @@ jobs: AzureWebJobsCosmosDBConnectionString: ${{ secrets.LinuxCosmosDBConnectionString39 }} AzureWebJobsEventHubConnectionString: ${{ secrets.LinuxEventHubConnectionString39 }} AzureWebJobsServiceBusConnectionString: ${{ secrets.LinuxServiceBusConnectionString39 }} + AzureWebJobsSqlConnectionString: ${{ secrets.LinuxSqlConnectionString39 }} AzureWebJobsEventGridTopicUri: ${{ secrets.LinuxEventGridTopicUriString39 }} AzureWebJobsEventGridConnectionKey: ${{ secrets.LinuxEventGridConnectionKeyString39 }} run: | diff --git a/azure_functions_worker/testutils.py b/azure_functions_worker/testutils.py index e92108c5..480abc24 100644 --- a/azure_functions_worker/testutils.py +++ b/azure_functions_worker/testutils.py @@ -861,6 +861,10 @@ def popen_webhost(*, stdout, stderr, script_root=FUNCS_PATH, port=None): if servicebus: extra_env['AzureWebJobsServiceBusConnectionString'] = servicebus + sql = testconfig['azure'].get('sql_key') + if sql: + extra_env['AzureWebJobsSqlConnectionString'] = sql + eventgrid_topic_uri = testconfig['azure'].get('eventgrid_topic_uri') if eventgrid_topic_uri: extra_env['AzureWebJobsEventGridTopicUri'] = eventgrid_topic_uri diff --git a/tests/endtoend/sql_functions/sql_input/__init__.py b/tests/endtoend/sql_functions/sql_input/__init__.py new file mode 100644 index 00000000..d3582431 --- /dev/null +++ b/tests/endtoend/sql_functions/sql_input/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import azure.functions as func +import json + +def main(req: func.HttpRequest, rowList: func.SqlRowList) -> func.HttpResponse: + rows = list(map(lambda r: json.loads(r.to_json()), rowList)) + + return func.HttpResponse( + json.dumps(rows), + status_code=201, + mimetype="application/json" + ) \ No newline at end of file diff --git a/tests/endtoend/sql_functions/sql_input/function.json b/tests/endtoend/sql_functions/sql_input/function.json new file mode 100644 index 00000000..775703f8 --- /dev/null +++ b/tests/endtoend/sql_functions/sql_input/function.json @@ -0,0 +1,26 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "type": "httpTrigger", + "direction": "in", + "name": "req", + "authLevel": "anonymous", + "route": "getproducts/{cost}" + }, + { + "direction": "in", + "type": "sql", + "name": "rowList", + "commandText": "SELECT * FROM Products WHERE Cost = @Cost", + "commandType": "Text", + "parameters": "@Cost={cost}", + "connectionStringSetting": "SqlConnectionString" + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] + } \ No newline at end of file diff --git a/tests/endtoend/sql_functions/sql_output/__init__.py b/tests/endtoend/sql_functions/sql_output/__init__.py new file mode 100644 index 00000000..48d69488 --- /dev/null +++ b/tests/endtoend/sql_functions/sql_output/__init__.py @@ -0,0 +1,21 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import azure.functions as func +import collections + +def main(req: func.HttpRequest, product: func.Out[func.SqlRow]) -> func.HttpResponse: + row = func.SqlRow(Product(req.params["id"], req.params["name"],req.params["cost"])) + product.set(row) + return func.HttpResponse( + row.to_json(), + status_code=201, + mimetype="application/json" + ) + +class Product(collections.UserDict): + def __init__(self, productId, name, cost): + super().__init__() + self['ProductId'] = productId + self['Name'] = name + self['Cost'] = cost \ No newline at end of file diff --git a/tests/endtoend/sql_functions/sql_output/function.json b/tests/endtoend/sql_functions/sql_output/function.json new file mode 100644 index 00000000..3b4156ca --- /dev/null +++ b/tests/endtoend/sql_functions/sql_output/function.json @@ -0,0 +1,28 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "name": "req", + "direction": "in", + "type": "httpTrigger", + "methods": [ + "get", + "post" + ], + "route": "addproduct" + }, + { + "name": "$return", + "type": "http", + "direction": "out" + }, + { + "name": "product", + "type": "sql", + "direction": "out", + "commandText": "[dbo].[Products]", + "connectionStringSetting": "SqlConnectionString" + } + ] + } \ No newline at end of file diff --git a/tests/endtoend/test_sql_functions.py b/tests/endtoend/test_sql_functions.py new file mode 100644 index 00000000..4a39883a --- /dev/null +++ b/tests/endtoend/test_sql_functions.py @@ -0,0 +1,39 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import json +import time + +from azure_functions_worker import testutils + + +class TestSqlFunctions(testutils.WebHostTestCase): + + @classmethod + def get_script_dir(cls): + return testutils.E2E_TESTS_FOLDER / 'sql_functions' + + @testutils.retryable_test(3, 5) + def test_sql_output_and_input(self): + time.sleep(5) + row = {'id': '1', 'name': 'test', 'cost': 100} + r = self.webhost.request('POST', 'add_product', + data=json.dumps(row)) + self.assertEqual(r.status_code, 201) + + max_retries = 10 + + for try_no in range(max_retries): + try: + r = self.webhost.request('GET', 'get_product') + self.assertEqual(r.status_code, 201) + response = r.json() + + self.assertEqual( + response, + row + ) + except AssertionError: + if try_no == max_retries - 1: + raise + else: + break From aaf2b2e9e02cd153ef422460a1c642fb4c29df7e Mon Sep 17 00:00:00 2001 From: luczhan Date: Wed, 11 May 2022 09:52:34 -0700 Subject: [PATCH 2/8] update test --- .../sql_functions/sql_input/function.json | 9 ++++---- .../sql_functions/sql_output/function.json | 8 +++---- tests/endtoend/test_sql_functions.py | 23 ++++--------------- 3 files changed, 12 insertions(+), 28 deletions(-) diff --git a/tests/endtoend/sql_functions/sql_input/function.json b/tests/endtoend/sql_functions/sql_input/function.json index 775703f8..8879bec3 100644 --- a/tests/endtoend/sql_functions/sql_input/function.json +++ b/tests/endtoend/sql_functions/sql_input/function.json @@ -6,16 +6,17 @@ "direction": "in", "name": "req", "authLevel": "anonymous", - "route": "getproducts/{cost}" + "methods": [ + "get" + ] }, { "direction": "in", "type": "sql", "name": "rowList", - "commandText": "SELECT * FROM Products WHERE Cost = @Cost", + "commandText": "SELECT * FROM Products", "commandType": "Text", - "parameters": "@Cost={cost}", - "connectionStringSetting": "SqlConnectionString" + "connectionStringSetting": "AzureWebJobsSqlConnectionString" }, { "type": "http", diff --git a/tests/endtoend/sql_functions/sql_output/function.json b/tests/endtoend/sql_functions/sql_output/function.json index 3b4156ca..53e756fa 100644 --- a/tests/endtoend/sql_functions/sql_output/function.json +++ b/tests/endtoend/sql_functions/sql_output/function.json @@ -7,10 +7,8 @@ "direction": "in", "type": "httpTrigger", "methods": [ - "get", - "post" - ], - "route": "addproduct" + "get" + ] }, { "name": "$return", @@ -22,7 +20,7 @@ "type": "sql", "direction": "out", "commandText": "[dbo].[Products]", - "connectionStringSetting": "SqlConnectionString" + "connectionStringSetting": "AzureWebJobsSqlConnectionString" } ] } \ No newline at end of file diff --git a/tests/endtoend/test_sql_functions.py b/tests/endtoend/test_sql_functions.py index 4a39883a..df0e866a 100644 --- a/tests/endtoend/test_sql_functions.py +++ b/tests/endtoend/test_sql_functions.py @@ -14,26 +14,11 @@ def get_script_dir(cls): @testutils.retryable_test(3, 5) def test_sql_output_and_input(self): - time.sleep(5) row = {'id': '1', 'name': 'test', 'cost': 100} - r = self.webhost.request('POST', 'add_product', + r = self.webhost.request('GET', 'sql_output', data=json.dumps(row)) self.assertEqual(r.status_code, 201) - max_retries = 10 - - for try_no in range(max_retries): - try: - r = self.webhost.request('GET', 'get_product') - self.assertEqual(r.status_code, 201) - response = r.json() - - self.assertEqual( - response, - row - ) - except AssertionError: - if try_no == max_retries - 1: - raise - else: - break + r = self.webhost.request('GET', 'sql_input') + self.assertEqual(r.status_code, 201) + self.assertEqual(r.text, row) From bd475fa7e56470173e1d97ac049f26ea821644aa Mon Sep 17 00:00:00 2001 From: luczhan Date: Tue, 10 May 2022 11:59:54 -0700 Subject: [PATCH 3/8] add sql bindings e2e test --- .ci/e2e_integration_test/pipeline.yml | 1 + .ci/linux_devops_e2e_tests.sh | 1 + .github/workflows/ci_e2e_workflow.yml | 4 ++ azure_functions_worker/testutils.py | 4 ++ .../sql_functions/sql_input/__init__.py | 14 +++++++ .../sql_functions/sql_input/function.json | 26 +++++++++++++ .../sql_functions/sql_output/__init__.py | 21 ++++++++++ .../sql_functions/sql_output/function.json | 28 +++++++++++++ tests/endtoend/test_sql_functions.py | 39 +++++++++++++++++++ 9 files changed, 138 insertions(+) create mode 100644 tests/endtoend/sql_functions/sql_input/__init__.py create mode 100644 tests/endtoend/sql_functions/sql_input/function.json create mode 100644 tests/endtoend/sql_functions/sql_output/__init__.py create mode 100644 tests/endtoend/sql_functions/sql_output/function.json create mode 100644 tests/endtoend/test_sql_functions.py diff --git a/.ci/e2e_integration_test/pipeline.yml b/.ci/e2e_integration_test/pipeline.yml index 181e7357..8117a541 100644 --- a/.ci/e2e_integration_test/pipeline.yml +++ b/.ci/e2e_integration_test/pipeline.yml @@ -42,6 +42,7 @@ steps: AzureWebJobsCosmosDBConnectionString: $(AzureWebJobsCosmosDBConnectionString) AzureWebJobsEventHubConnectionString: $(AzureWebJobsEventHubConnectionString) AzureWebJobsServiceBusConnectionString: $(AzureWebJobsServiceBusConnectionString) + AzureWebJobsSqlConnectionString: $(AzureWebJobsSqlConnectionString) AzureWebJobsEventGridTopicUri: $(AzureWebJobsEventGridTopicUri) AzureWebJobsEventGridConnectionKey: $(AzureWebJobsEventGridConnectionKey) PythonVersion: $(PYTHON_VERSION) diff --git a/.ci/linux_devops_e2e_tests.sh b/.ci/linux_devops_e2e_tests.sh index 83939d38..7a5d5105 100644 --- a/.ci/linux_devops_e2e_tests.sh +++ b/.ci/linux_devops_e2e_tests.sh @@ -5,6 +5,7 @@ export AzureWebJobsStorage=$LINUXSTORAGECONNECTIONSTRING export AzureWebJobsCosmosDBConnectionString=$LINUXCOSMOSDBCONNECTIONSTRING export AzureWebJobsEventHubConnectionString=$LINUXEVENTHUBCONNECTIONSTRING export AzureWebJobsServiceBusConnectionString=$LINUXSERVICEBUSCONNECTIONSTRING +export AzureWebJobsSqlConnectionString=$LINUXSQLCONNECTIONSTRING export AzureWebJobsEventGridTopicUri=$LINUXEVENTGRIDTOPICURI export AzureWebJobsEventGridConnectionKey=$LINUXEVENTGRIDTOPICCONNECTIONKEY diff --git a/.github/workflows/ci_e2e_workflow.yml b/.github/workflows/ci_e2e_workflow.yml index 6b783c58..201f678a 100644 --- a/.github/workflows/ci_e2e_workflow.yml +++ b/.github/workflows/ci_e2e_workflow.yml @@ -76,6 +76,7 @@ jobs: AzureWebJobsCosmosDBConnectionString: ${{ secrets.LinuxCosmosDBConnectionString36 }} AzureWebJobsEventHubConnectionString: ${{ secrets.LinuxEventHubConnectionString36 }} AzureWebJobsServiceBusConnectionString: ${{ secrets.LinuxServiceBusConnectionString36 }} + AzureWebJobsSqlConnectionString: ${{ secrets.LinuxSqlConnectionString36 }} AzureWebJobsEventGridTopicUri: ${{ secrets.LinuxEventGridTopicUriString36 }} AzureWebJobsEventGridConnectionKey: ${{ secrets.LinuxEventGridConnectionKeyString36 }} run: | @@ -87,6 +88,7 @@ jobs: AzureWebJobsCosmosDBConnectionString: ${{ secrets.LinuxCosmosDBConnectionString37 }} AzureWebJobsEventHubConnectionString: ${{ secrets.LinuxEventHubConnectionString37 }} AzureWebJobsServiceBusConnectionString: ${{ secrets.LinuxServiceBusConnectionString37 }} + AzureWebJobsSqlConnectionString: ${{ secrets.LinuxSqlConnectionString37 }} AzureWebJobsEventGridTopicUri: ${{ secrets.LinuxEventGridTopicUriString37 }} AzureWebJobsEventGridConnectionKey: ${{ secrets.LinuxEventGridConnectionKeyString37 }} run: | @@ -98,6 +100,7 @@ jobs: AzureWebJobsCosmosDBConnectionString: ${{ secrets.LinuxCosmosDBConnectionString38 }} AzureWebJobsEventHubConnectionString: ${{ secrets.LinuxEventHubConnectionString38 }} AzureWebJobsServiceBusConnectionString: ${{ secrets.LinuxServiceBusConnectionString38 }} + AzureWebJobsSqlConnectionString: ${{ secrets.LinuxSqlConnectionString38 }} AzureWebJobsEventGridTopicUri: ${{ secrets.LinuxEventGridTopicUriString38 }} AzureWebJobsEventGridConnectionKey: ${{ secrets.LinuxEventGridConnectionKeyString38 }} run: | @@ -109,6 +112,7 @@ jobs: AzureWebJobsCosmosDBConnectionString: ${{ secrets.LinuxCosmosDBConnectionString39 }} AzureWebJobsEventHubConnectionString: ${{ secrets.LinuxEventHubConnectionString39 }} AzureWebJobsServiceBusConnectionString: ${{ secrets.LinuxServiceBusConnectionString39 }} + AzureWebJobsSqlConnectionString: ${{ secrets.LinuxSqlConnectionString39 }} AzureWebJobsEventGridTopicUri: ${{ secrets.LinuxEventGridTopicUriString39 }} AzureWebJobsEventGridConnectionKey: ${{ secrets.LinuxEventGridConnectionKeyString39 }} run: | diff --git a/azure_functions_worker/testutils.py b/azure_functions_worker/testutils.py index 62e14a2e..a74333bf 100644 --- a/azure_functions_worker/testutils.py +++ b/azure_functions_worker/testutils.py @@ -878,6 +878,10 @@ def popen_webhost(*, stdout, stderr, script_root=FUNCS_PATH, port=None): if servicebus: extra_env['AzureWebJobsServiceBusConnectionString'] = servicebus + sql = testconfig['azure'].get('sql_key') + if sql: + extra_env['AzureWebJobsSqlConnectionString'] = sql + eventgrid_topic_uri = testconfig['azure'].get('eventgrid_topic_uri') if eventgrid_topic_uri: extra_env['AzureWebJobsEventGridTopicUri'] = eventgrid_topic_uri diff --git a/tests/endtoend/sql_functions/sql_input/__init__.py b/tests/endtoend/sql_functions/sql_input/__init__.py new file mode 100644 index 00000000..d3582431 --- /dev/null +++ b/tests/endtoend/sql_functions/sql_input/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import azure.functions as func +import json + +def main(req: func.HttpRequest, rowList: func.SqlRowList) -> func.HttpResponse: + rows = list(map(lambda r: json.loads(r.to_json()), rowList)) + + return func.HttpResponse( + json.dumps(rows), + status_code=201, + mimetype="application/json" + ) \ No newline at end of file diff --git a/tests/endtoend/sql_functions/sql_input/function.json b/tests/endtoend/sql_functions/sql_input/function.json new file mode 100644 index 00000000..775703f8 --- /dev/null +++ b/tests/endtoend/sql_functions/sql_input/function.json @@ -0,0 +1,26 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "type": "httpTrigger", + "direction": "in", + "name": "req", + "authLevel": "anonymous", + "route": "getproducts/{cost}" + }, + { + "direction": "in", + "type": "sql", + "name": "rowList", + "commandText": "SELECT * FROM Products WHERE Cost = @Cost", + "commandType": "Text", + "parameters": "@Cost={cost}", + "connectionStringSetting": "SqlConnectionString" + }, + { + "type": "http", + "direction": "out", + "name": "$return" + } + ] + } \ No newline at end of file diff --git a/tests/endtoend/sql_functions/sql_output/__init__.py b/tests/endtoend/sql_functions/sql_output/__init__.py new file mode 100644 index 00000000..48d69488 --- /dev/null +++ b/tests/endtoend/sql_functions/sql_output/__init__.py @@ -0,0 +1,21 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import azure.functions as func +import collections + +def main(req: func.HttpRequest, product: func.Out[func.SqlRow]) -> func.HttpResponse: + row = func.SqlRow(Product(req.params["id"], req.params["name"],req.params["cost"])) + product.set(row) + return func.HttpResponse( + row.to_json(), + status_code=201, + mimetype="application/json" + ) + +class Product(collections.UserDict): + def __init__(self, productId, name, cost): + super().__init__() + self['ProductId'] = productId + self['Name'] = name + self['Cost'] = cost \ No newline at end of file diff --git a/tests/endtoend/sql_functions/sql_output/function.json b/tests/endtoend/sql_functions/sql_output/function.json new file mode 100644 index 00000000..3b4156ca --- /dev/null +++ b/tests/endtoend/sql_functions/sql_output/function.json @@ -0,0 +1,28 @@ +{ + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "anonymous", + "name": "req", + "direction": "in", + "type": "httpTrigger", + "methods": [ + "get", + "post" + ], + "route": "addproduct" + }, + { + "name": "$return", + "type": "http", + "direction": "out" + }, + { + "name": "product", + "type": "sql", + "direction": "out", + "commandText": "[dbo].[Products]", + "connectionStringSetting": "SqlConnectionString" + } + ] + } \ No newline at end of file diff --git a/tests/endtoend/test_sql_functions.py b/tests/endtoend/test_sql_functions.py new file mode 100644 index 00000000..4a39883a --- /dev/null +++ b/tests/endtoend/test_sql_functions.py @@ -0,0 +1,39 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. +import json +import time + +from azure_functions_worker import testutils + + +class TestSqlFunctions(testutils.WebHostTestCase): + + @classmethod + def get_script_dir(cls): + return testutils.E2E_TESTS_FOLDER / 'sql_functions' + + @testutils.retryable_test(3, 5) + def test_sql_output_and_input(self): + time.sleep(5) + row = {'id': '1', 'name': 'test', 'cost': 100} + r = self.webhost.request('POST', 'add_product', + data=json.dumps(row)) + self.assertEqual(r.status_code, 201) + + max_retries = 10 + + for try_no in range(max_retries): + try: + r = self.webhost.request('GET', 'get_product') + self.assertEqual(r.status_code, 201) + response = r.json() + + self.assertEqual( + response, + row + ) + except AssertionError: + if try_no == max_retries - 1: + raise + else: + break From 2b445f58f4227a0d01708857bd04a33a7b98ba41 Mon Sep 17 00:00:00 2001 From: luczhan Date: Wed, 11 May 2022 09:52:34 -0700 Subject: [PATCH 4/8] update test --- .../sql_functions/sql_input/function.json | 9 ++++---- .../sql_functions/sql_output/function.json | 8 +++---- tests/endtoend/test_sql_functions.py | 23 ++++--------------- 3 files changed, 12 insertions(+), 28 deletions(-) diff --git a/tests/endtoend/sql_functions/sql_input/function.json b/tests/endtoend/sql_functions/sql_input/function.json index 775703f8..8879bec3 100644 --- a/tests/endtoend/sql_functions/sql_input/function.json +++ b/tests/endtoend/sql_functions/sql_input/function.json @@ -6,16 +6,17 @@ "direction": "in", "name": "req", "authLevel": "anonymous", - "route": "getproducts/{cost}" + "methods": [ + "get" + ] }, { "direction": "in", "type": "sql", "name": "rowList", - "commandText": "SELECT * FROM Products WHERE Cost = @Cost", + "commandText": "SELECT * FROM Products", "commandType": "Text", - "parameters": "@Cost={cost}", - "connectionStringSetting": "SqlConnectionString" + "connectionStringSetting": "AzureWebJobsSqlConnectionString" }, { "type": "http", diff --git a/tests/endtoend/sql_functions/sql_output/function.json b/tests/endtoend/sql_functions/sql_output/function.json index 3b4156ca..53e756fa 100644 --- a/tests/endtoend/sql_functions/sql_output/function.json +++ b/tests/endtoend/sql_functions/sql_output/function.json @@ -7,10 +7,8 @@ "direction": "in", "type": "httpTrigger", "methods": [ - "get", - "post" - ], - "route": "addproduct" + "get" + ] }, { "name": "$return", @@ -22,7 +20,7 @@ "type": "sql", "direction": "out", "commandText": "[dbo].[Products]", - "connectionStringSetting": "SqlConnectionString" + "connectionStringSetting": "AzureWebJobsSqlConnectionString" } ] } \ No newline at end of file diff --git a/tests/endtoend/test_sql_functions.py b/tests/endtoend/test_sql_functions.py index 4a39883a..df0e866a 100644 --- a/tests/endtoend/test_sql_functions.py +++ b/tests/endtoend/test_sql_functions.py @@ -14,26 +14,11 @@ def get_script_dir(cls): @testutils.retryable_test(3, 5) def test_sql_output_and_input(self): - time.sleep(5) row = {'id': '1', 'name': 'test', 'cost': 100} - r = self.webhost.request('POST', 'add_product', + r = self.webhost.request('GET', 'sql_output', data=json.dumps(row)) self.assertEqual(r.status_code, 201) - max_retries = 10 - - for try_no in range(max_retries): - try: - r = self.webhost.request('GET', 'get_product') - self.assertEqual(r.status_code, 201) - response = r.json() - - self.assertEqual( - response, - row - ) - except AssertionError: - if try_no == max_retries - 1: - raise - else: - break + r = self.webhost.request('GET', 'sql_input') + self.assertEqual(r.status_code, 201) + self.assertEqual(r.text, row) From 3e98927040b58a6c530e5b16e61f8e54ace06971 Mon Sep 17 00:00:00 2001 From: Lucy Zhang Date: Thu, 21 Jul 2022 15:14:42 -0700 Subject: [PATCH 5/8] fix sql binding test --- azure_functions_worker/testutils.py | 2 +- setup.py | 2 + .../sql_functions/sql_input/__init__.py | 10 ++-- .../sql_functions/sql_input/function.json | 52 +++++++++---------- .../sql_functions/sql_output/__init__.py | 15 ++---- .../sql_functions/sql_output/function.json | 50 +++++++++--------- tests/endtoend/test_sql_functions.py | 9 ++-- 7 files changed, 68 insertions(+), 72 deletions(-) diff --git a/azure_functions_worker/testutils.py b/azure_functions_worker/testutils.py index a74333bf..6641e270 100644 --- a/azure_functions_worker/testutils.py +++ b/azure_functions_worker/testutils.py @@ -881,7 +881,7 @@ def popen_webhost(*, stdout, stderr, script_root=FUNCS_PATH, port=None): sql = testconfig['azure'].get('sql_key') if sql: extra_env['AzureWebJobsSqlConnectionString'] = sql - + eventgrid_topic_uri = testconfig['azure'].get('eventgrid_topic_uri') if eventgrid_topic_uri: extra_env['AzureWebJobsEventGridTopicUri'] = eventgrid_topic_uri diff --git a/setup.py b/setup.py index 0ffe06ec..9d4b5048 100644 --- a/setup.py +++ b/setup.py @@ -50,6 +50,8 @@ Version="4.0.5" /> + diff --git a/tests/endtoend/sql_functions/sql_input/__init__.py b/tests/endtoend/sql_functions/sql_input/__init__.py index d3582431..b87950e4 100644 --- a/tests/endtoend/sql_functions/sql_input/__init__.py +++ b/tests/endtoend/sql_functions/sql_input/__init__.py @@ -1,14 +1,14 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -import azure.functions as func import json +import azure.functions as func -def main(req: func.HttpRequest, rowList: func.SqlRowList) -> func.HttpResponse: - rows = list(map(lambda r: json.loads(r.to_json()), rowList)) +def main(req: func.HttpRequest, products: func.SqlRowList) -> func.HttpResponse: + rows = list(map(lambda r: json.loads(r.to_json()), products)) return func.HttpResponse( json.dumps(rows), - status_code=201, + status_code=200, mimetype="application/json" - ) \ No newline at end of file + ) diff --git a/tests/endtoend/sql_functions/sql_input/function.json b/tests/endtoend/sql_functions/sql_input/function.json index 8879bec3..1c5e9d55 100644 --- a/tests/endtoend/sql_functions/sql_input/function.json +++ b/tests/endtoend/sql_functions/sql_input/function.json @@ -1,27 +1,27 @@ { - "scriptFile": "__init__.py", - "bindings": [ - { - "type": "httpTrigger", - "direction": "in", - "name": "req", - "authLevel": "anonymous", - "methods": [ - "get" - ] - }, - { - "direction": "in", - "type": "sql", - "name": "rowList", - "commandText": "SELECT * FROM Products", - "commandType": "Text", - "connectionStringSetting": "AzureWebJobsSqlConnectionString" - }, - { - "type": "http", - "direction": "out", - "name": "$return" - } - ] - } \ No newline at end of file + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "function", + "name": "req", + "type": "httpTrigger", + "direction": "in", + "methods": [ + "get" + ] + }, + { + "name": "$return", + "type": "http", + "direction": "out" + }, + { + "name": "products", + "type": "sql", + "direction": "in", + "commandText": "SELECT * FROM Products", + "commandType": "Text", + "connectionStringSetting": "AzureWebJobsSqlConnectionString" + } + ] +} \ No newline at end of file diff --git a/tests/endtoend/sql_functions/sql_output/__init__.py b/tests/endtoend/sql_functions/sql_output/__init__.py index 48d69488..f499d724 100644 --- a/tests/endtoend/sql_functions/sql_output/__init__.py +++ b/tests/endtoend/sql_functions/sql_output/__init__.py @@ -1,21 +1,16 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. +import json import azure.functions as func -import collections def main(req: func.HttpRequest, product: func.Out[func.SqlRow]) -> func.HttpResponse: - row = func.SqlRow(Product(req.params["id"], req.params["name"],req.params["cost"])) + body = json.loads(req.get_body()) + row = func.SqlRow.from_dict(body) product.set(row) + return func.HttpResponse( - row.to_json(), + body=req.get_body(), status_code=201, mimetype="application/json" ) - -class Product(collections.UserDict): - def __init__(self, productId, name, cost): - super().__init__() - self['ProductId'] = productId - self['Name'] = name - self['Cost'] = cost \ No newline at end of file diff --git a/tests/endtoend/sql_functions/sql_output/function.json b/tests/endtoend/sql_functions/sql_output/function.json index 53e756fa..59fe4a4b 100644 --- a/tests/endtoend/sql_functions/sql_output/function.json +++ b/tests/endtoend/sql_functions/sql_output/function.json @@ -1,26 +1,26 @@ { - "scriptFile": "__init__.py", - "bindings": [ - { - "authLevel": "anonymous", - "name": "req", - "direction": "in", - "type": "httpTrigger", - "methods": [ - "get" - ] - }, - { - "name": "$return", - "type": "http", - "direction": "out" - }, - { - "name": "product", - "type": "sql", - "direction": "out", - "commandText": "[dbo].[Products]", - "connectionStringSetting": "AzureWebJobsSqlConnectionString" - } - ] - } \ No newline at end of file + "scriptFile": "__init__.py", + "bindings": [ + { + "authLevel": "function", + "name": "req", + "type": "httpTrigger", + "direction": "in", + "methods": [ + "post" + ] + }, + { + "name": "$return", + "type": "http", + "direction": "out" + }, + { + "name": "product", + "type": "sql", + "direction": "out", + "commandText": "[dbo].[Products]", + "connectionStringSetting": "AzureWebJobsSqlConnectionString" + } + ] +} diff --git a/tests/endtoend/test_sql_functions.py b/tests/endtoend/test_sql_functions.py index df0e866a..ba0e3a36 100644 --- a/tests/endtoend/test_sql_functions.py +++ b/tests/endtoend/test_sql_functions.py @@ -1,7 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. import json -import time from azure_functions_worker import testutils @@ -14,11 +13,11 @@ def get_script_dir(cls): @testutils.retryable_test(3, 5) def test_sql_output_and_input(self): - row = {'id': '1', 'name': 'test', 'cost': 100} - r = self.webhost.request('GET', 'sql_output', + row = {"ProductId": 0, "Name": "test", "Cost": 100} + r = self.webhost.request('POST', 'sql_output', data=json.dumps(row)) self.assertEqual(r.status_code, 201) r = self.webhost.request('GET', 'sql_input') - self.assertEqual(r.status_code, 201) - self.assertEqual(r.text, row) + self.assertEqual(r.status_code, 200) + self.assertEqual(r.text, "[{\"ProductId\": 0, \"Name\": \"test\", \"Cost\": 100}]") From 13ce79504a1713fc4b69a07f0055af09b7642c7e Mon Sep 17 00:00:00 2001 From: Lucy Zhang Date: Mon, 25 Jul 2022 07:00:37 -0700 Subject: [PATCH 6/8] add python310 sql connection string --- .github/workflows/ci_e2e_workflow.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci_e2e_workflow.yml b/.github/workflows/ci_e2e_workflow.yml index 201f678a..10b86bfc 100644 --- a/.github/workflows/ci_e2e_workflow.yml +++ b/.github/workflows/ci_e2e_workflow.yml @@ -124,6 +124,7 @@ jobs: AzureWebJobsCosmosDBConnectionString: ${{ secrets.LinuxCosmosDBConnectionString310 }} AzureWebJobsEventHubConnectionString: ${{ secrets.LinuxEventHubConnectionString310 }} AzureWebJobsServiceBusConnectionString: ${{ secrets.LinuxServiceBusConnectionString310 }} + AzureWebJobsSqlConnectionString: ${{ secrets.LinuxSqlConnectionString310 }} AzureWebJobsEventGridTopicUri: ${{ secrets.LinuxEventGridTopicUriString310 }} AzureWebJobsEventGridConnectionKey: ${{ secrets.LinuxEventGridConnectionKeyString310 }} run: | From b7142edb274d0e06c2505647a33d35a90510a5bb Mon Sep 17 00:00:00 2001 From: Lucy Zhang Date: Mon, 25 Jul 2022 15:36:04 -0700 Subject: [PATCH 7/8] fix unit tests --- tests/endtoend/sql_functions/sql_input/__init__.py | 1 + tests/endtoend/sql_functions/sql_output/__init__.py | 5 +++-- tests/endtoend/test_sql_functions.py | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/endtoend/sql_functions/sql_input/__init__.py b/tests/endtoend/sql_functions/sql_input/__init__.py index b87950e4..a8e3c3b3 100644 --- a/tests/endtoend/sql_functions/sql_input/__init__.py +++ b/tests/endtoend/sql_functions/sql_input/__init__.py @@ -4,6 +4,7 @@ import json import azure.functions as func + def main(req: func.HttpRequest, products: func.SqlRowList) -> func.HttpResponse: rows = list(map(lambda r: json.loads(r.to_json()), products)) diff --git a/tests/endtoend/sql_functions/sql_output/__init__.py b/tests/endtoend/sql_functions/sql_output/__init__.py index f499d724..1a8226b5 100644 --- a/tests/endtoend/sql_functions/sql_output/__init__.py +++ b/tests/endtoend/sql_functions/sql_output/__init__.py @@ -4,10 +4,11 @@ import json import azure.functions as func -def main(req: func.HttpRequest, product: func.Out[func.SqlRow]) -> func.HttpResponse: + +def main(req: func.HttpRequest, r: func.Out[func.SqlRow]) -> func.HttpResponse: body = json.loads(req.get_body()) row = func.SqlRow.from_dict(body) - product.set(row) + r.set(row) return func.HttpResponse( body=req.get_body(), diff --git a/tests/endtoend/test_sql_functions.py b/tests/endtoend/test_sql_functions.py index ba0e3a36..ba8c442c 100644 --- a/tests/endtoend/test_sql_functions.py +++ b/tests/endtoend/test_sql_functions.py @@ -20,4 +20,5 @@ def test_sql_output_and_input(self): r = self.webhost.request('GET', 'sql_input') self.assertEqual(r.status_code, 200) - self.assertEqual(r.text, "[{\"ProductId\": 0, \"Name\": \"test\", \"Cost\": 100}]") + expectedText = "[{\"ProductId\": 0, \"Name\": \"test\", \"Cost\": 100}]" + self.assertEqual(r.text, expectedText) From 8a5d372ef304633890868450f8f5074eef509daa Mon Sep 17 00:00:00 2001 From: Lucy Zhang Date: Tue, 26 Jul 2022 14:06:11 -0700 Subject: [PATCH 8/8] fix sql end to end test --- tests/endtoend/sql_functions/sql_output/function.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/endtoend/sql_functions/sql_output/function.json b/tests/endtoend/sql_functions/sql_output/function.json index 59fe4a4b..e58c18cf 100644 --- a/tests/endtoend/sql_functions/sql_output/function.json +++ b/tests/endtoend/sql_functions/sql_output/function.json @@ -16,7 +16,7 @@ "direction": "out" }, { - "name": "product", + "name": "r", "type": "sql", "direction": "out", "commandText": "[dbo].[Products]",