From bccab2045bb9be3c15f1fe09ee15f839bc088023 Mon Sep 17 00:00:00 2001 From: kaibocai <89094811+kaibocai@users.noreply.github.com> Date: Tue, 16 Jan 2024 22:42:06 -0600 Subject: [PATCH] Add end to end test for SQL Bindings (#742) * add sql e2e test * remove sql trigger test * check sql input result * update sql java library version to 2.1.0 * remove sql trigger function * remove GetProducts2 * Updating library version. Fixes merge conflict --------- Co-authored-by: Lucy Zhang Co-authored-by: Shreyas Gopalakrishna Co-authored-by: Shreyas Gopalakrishna <11889130+shreyas-gopalakrishna@users.noreply.github.com> --- azure-pipelines-e2e-integration-tests.yml | 1 + azure-pipelines.yml | 1 + .../Constants.cs | 15 +++-- .../SqlEndToEndTests.cs | 44 +++++++++++++ .../Utilities.cs | 24 +++++++ endtoendtests/local.settings.json | 1 + endtoendtests/pom.xml | 6 ++ .../functions/endtoend/SqlTriggerTests.java | 62 +++++++++++++++++++ 8 files changed, 148 insertions(+), 6 deletions(-) create mode 100644 endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/SqlEndToEndTests.cs create mode 100644 endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/SqlTriggerTests.java diff --git a/azure-pipelines-e2e-integration-tests.yml b/azure-pipelines-e2e-integration-tests.yml index 907ef36b..d0249f19 100644 --- a/azure-pipelines-e2e-integration-tests.yml +++ b/azure-pipelines-e2e-integration-tests.yml @@ -149,6 +149,7 @@ jobs: JAVA_HOME: $(JavaHome) AzureWebJobsStorage: $(AzureWebJobsStorage) AzureWebJobsCosmosDBConnectionString: $(AzureWebJobsCosmosDBConnectionString) + AzureWebJobsSqlConnectionString: $(AzureWebJobsSqlConnectionString) AzureWebJobsServiceBus: $(AzureWebJobsServiceBus) AzureWebJobsEventHubSender_2: $(AzureWebJobsEventHubSender_2) AzureWebJobsEventHubReceiver: $(AzureWebJobsEventHubReceiver) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 30249761..9ebdf5ee 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -211,6 +211,7 @@ jobs: JAVA_HOME: $(JavaHome) AzureWebJobsStorage: $(AzureWebJobsStorage) AzureWebJobsCosmosDBConnectionString: $(AzureWebJobsCosmosDBConnectionString) + AzureWebJobsSqlConnectionString: $(AzureWebJobsSqlConnectionString) AzureWebJobsServiceBus: $(AzureWebJobsServiceBus) AzureWebJobsEventHubReceiver: $(AzureWebJobsEventHubReceiver) AzureWebJobsEventHubSender_2: $(AzureWebJobsEventHubSender_2) diff --git a/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Constants.cs b/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Constants.cs index fb82b718..4a0ecb63 100644 --- a/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Constants.cs +++ b/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Constants.cs @@ -8,7 +8,7 @@ namespace Azure.Functions.Java.Tests.E2E public static class Constants { public static string FunctionsHostUrl = Environment.GetEnvironmentVariable("FunctionAppUrl") ?? "http://localhost:7071"; - public static string StorageConnectionStringSetting = Environment.GetEnvironmentVariable("AzureWebJobsStorage"); + public static string StorageConnectionStringSetting = Environment.GetEnvironmentVariable("AzureWebJobsStorage"); //Queue tests @@ -87,11 +87,14 @@ public static class Constants public static string ServiceBusConnectionStringSetting = Environment.GetEnvironmentVariable("AzureWebJobsServiceBus"); // Xunit Fixtures and Collections - public const string FunctionAppCollectionName = "FunctionAppCollection"; - - // Application Insights - public static string ApplicationInsightAPIKey = Environment.GetEnvironmentVariable("ApplicationInsightAPIKey"); - public static string ApplicationInsightAPPID = Environment.GetEnvironmentVariable("ApplicationInsightAPPID"); + public const string FunctionAppCollectionName = "FunctionAppCollection"; + + // Application Insights + public static string ApplicationInsightAPIKey = Environment.GetEnvironmentVariable("ApplicationInsightAPIKey"); + public static string ApplicationInsightAPPID = Environment.GetEnvironmentVariable("ApplicationInsightAPPID"); public static string ApplicationInsightAgentVersion = Environment.GetEnvironmentVariable("ApplicationInsightAgentVersion"); + + // SQL Binding tests + public static string SqlConnectionStringSetting = Environment.GetEnvironmentVariable("AzureWebJobsSqlConnectionString"); } } diff --git a/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/SqlEndToEndTests.cs b/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/SqlEndToEndTests.cs new file mode 100644 index 00000000..4b5ffe5c --- /dev/null +++ b/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/SqlEndToEndTests.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace Azure.Functions.Java.Tests.E2E +{ + [Collection(Constants.FunctionAppCollectionName)] + public class SqlEndToEndTests + { + private readonly FunctionAppFixture _fixture; + + public SqlEndToEndTests(FunctionAppFixture fixture) + { + this._fixture = fixture; + } + + [Fact] + public async Task SqlInput_Output_Succeeds() + { + TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1); + int id = (int) t.TotalSeconds; + var product = new Dictionary() + { + { "ProductId", id }, + { "Name", "test" }, + { "Cost", 100 } + }; + + var productString = JsonConvert.SerializeObject(product); + // Insert row into Products table using SqlOutput + Assert.True(await Utilities.InvokeHttpTriggerPost("AddProduct", productString, HttpStatusCode.OK)); + + // Read row from Products table using SqlInput + Assert.True(await Utilities.InvokeHttpTrigger("GetProducts", "/" + id.ToString(), HttpStatusCode.OK, productString)); + } + } +} diff --git a/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Utilities.cs b/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Utilities.cs index c232b99c..8994c380 100644 --- a/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Utilities.cs +++ b/endtoendtests/Azure.Functions.Java.Tests.E2E/Azure.Functions.Java.Tests.E2E/Utilities.cs @@ -64,6 +64,30 @@ public static async Task InvokeHttpTrigger(string functionName, string que } } + public static async Task InvokeHttpTriggerPost(string functionName, string bodyString, HttpStatusCode expectedStatusCode) + { + string uri = $"{Constants.FunctionsHostUrl}/api/{functionName}"; + using (var request = new HttpRequestMessage(HttpMethod.Post, uri)) + { + request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("text/plain")); + request.Content = new StringContent( + bodyString, + Encoding.UTF8, + "application/json" + ); + var response = await httpClient.SendAsync(request); + + Console.WriteLine( + $"InvokeHttpTrigger: {functionName} : {response.StatusCode} : {response.ReasonPhrase}"); + if (expectedStatusCode != response.StatusCode) + { + return false; + } + + return true; + } + } + public static async Task InvokeEventGridTrigger(string functionName, JObject jsonContent, HttpStatusCode expectedStatusCode=HttpStatusCode.Accepted) { string uri = $"{Constants.FunctionsHostUrl}/runtime/webhooks/eventgrid?functionName={functionName}"; diff --git a/endtoendtests/local.settings.json b/endtoendtests/local.settings.json index 81517e86..5d4d5201 100644 --- a/endtoendtests/local.settings.json +++ b/endtoendtests/local.settings.json @@ -17,6 +17,7 @@ "AzureWebJobsCosmosDBConnectionString":"", "AzureWebJobsEventGridOutputBindingTopicUriString": "", "AzureWebJobsEventGridOutputBindingTopicKeyString": "", + "AzureWebJobsSqlConnectionString": "", "FUNCTIONS_WORKER_RUNTIME": "java" } } diff --git a/endtoendtests/pom.xml b/endtoendtests/pom.xml index b019d874..144a2e22 100644 --- a/endtoendtests/pom.xml +++ b/endtoendtests/pom.xml @@ -23,6 +23,7 @@ 1.8 1.18.0 3.1.0 + 2.1.0 1.0.0-beta.1 azure-functions-java-endtoendtests westus @@ -64,6 +65,11 @@ azure-functions-java-library ${azure.functions.java.library.version} + + com.microsoft.azure.functions + azure-functions-java-library-sql + ${azure.functions.java.library.sql.version} + com.microsoft durabletask-azure-functions diff --git a/endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/SqlTriggerTests.java b/endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/SqlTriggerTests.java new file mode 100644 index 00000000..93564bb4 --- /dev/null +++ b/endtoendtests/src/main/java/com/microsoft/azure/functions/endtoend/SqlTriggerTests.java @@ -0,0 +1,62 @@ +package com.microsoft.azure.functions.endtoend; + +import com.microsoft.azure.functions.annotation.*; +import com.google.gson.Gson; +import com.microsoft.azure.functions.*; +import com.microsoft.azure.functions.HttpMethod; +import com.microsoft.azure.functions.sql.annotation.CommandType; +import com.microsoft.azure.functions.sql.annotation.SQLInput; +import com.microsoft.azure.functions.sql.annotation.SQLOutput; +import com.microsoft.azure.functions.sql.annotation.SQLTrigger; + +import java.io.IOException; +import java.lang.reflect.Array; +import java.util.*; + +/** + * Azure Functions with Azure SQL DB. + */ +public class SqlTriggerTests { + + @FunctionName("GetProducts") + public HttpResponseMessage GetProducts(@HttpTrigger(name = "req", methods = { HttpMethod.GET, + HttpMethod.POST }, route = "getproducts/{productid}", authLevel = AuthorizationLevel.ANONYMOUS) + HttpRequestMessage> request, + @SQLInput(name = "products", commandText = "SELECT TOP 1 * FROM Products WHERE ProductId = @ProductId", + commandType = CommandType.Text, parameters = "@ProductId={productid}", + connectionStringSetting = "AzureWebJobsSqlConnectionString") Product[] products, + final ExecutionContext context) { + + context.getLogger().info("Java HTTP trigger processed a request."); + + if (products.length != 0) { + return request.createResponseBuilder(HttpStatus.OK).body(products[0].toString()).build(); + } else { + return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR) + .body("Did not find expected product in table Products").build(); + } + } + + @FunctionName("AddProduct") + public HttpResponseMessage AddProduct(@HttpTrigger(name = "req", methods = { HttpMethod.GET, + HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS) HttpRequestMessage> request, + @SQLOutput(name = "product", commandText = "Products", connectionStringSetting = "AzureWebJobsSqlConnectionString") OutputBinding product, + final ExecutionContext context) { + context.getLogger().info("Java HTTP trigger processed a request."); + + String json = request.getBody().get(); + product.setValue(new Gson().fromJson(json, Product.class)); + + return request.createResponseBuilder(HttpStatus.OK).body(product).build(); + } + + public class Product { + public int ProductId; + public String Name; + public int Cost; + + public String toString() { + return "{\"ProductId\":" + ProductId + ",\"Name\":\"" + Name + "\",\"Cost\":" + Cost + "}"; + } + } +}