Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

emulated tests support #790

Merged
merged 15 commits into from
Oct 19, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.27703.2042
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Functions.Java.Tests.E2E", "Azure.Functions.Java.Tests.E2E\Azure.Functions.Java.Tests.E2E.csproj", "{830D2261-3E35-46A6-8B52-08233D852ED0}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{830D2261-3E35-46A6-8B52-08233D852ED0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{830D2261-3E35-46A6-8B52-08233D852ED0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{830D2261-3E35-46A6-8B52-08233D852ED0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{830D2261-3E35-46A6-8B52-08233D852ED0}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {45378AFC-889B-4751-A494-44B73F8EA77E}
EndGlobalSection
EndGlobal
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="2.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="WindowsAzure.Storage" Version="9.3.2" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
</ItemGroup>

<ItemGroup>
<None Update="confluent_cloud_cacert.pem">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="testBlobTrigger.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;

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");


//Queue tests
public static string OutputBindingQueueName = "test-output-java";
public static string InputBindingQueueName = "test-input-java";
public static string OutputBindingQueueNamePOJO = "test-output-java-pojo";
public static string InputBindingQueueNamePOJO = "test-input-java-pojo";
public static string InputBindingQueueNameMetadata = "test-input-java-metadata";
public static string OutputBindingQueueNameMetadata = "test-output-java-metadata";
public static string TestQueueMessage = "Hello, World";

//Blob tests
public static string TriggerInputBindingBlobContainer = "test-triggerinput-java-new";
public static string InputBindingBlobContainer = "test-input-java-new";
public static string OutputBindingBlobContainer = "test-output-java-new";

// Xunit Fixtures and Collections
public const string FunctionAppCollectionName = "FunctionAppCollection";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;

namespace Azure.Functions.Java.Tests.E2E
{
public static class FixtureHelpers
{
public static Process GetFuncHostProcess(bool enableAuth = false)
{
var funcHostProcess = new Process();
var rootDir = Path.GetFullPath(@"../../../../../..");

funcHostProcess.StartInfo.UseShellExecute = false;
funcHostProcess.StartInfo.RedirectStandardError = true;
funcHostProcess.StartInfo.RedirectStandardOutput = true;
funcHostProcess.StartInfo.CreateNoWindow = true;
funcHostProcess.StartInfo.WorkingDirectory = Path.Combine(rootDir, @"emulatedtests/target/azure-functions/azure-functions-java-emulatedtests");
if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
funcHostProcess.StartInfo.FileName = Path.Combine(rootDir, @"Azure.Functions.Cli/func.exe");
}
else
{
funcHostProcess.StartInfo.FileName = Path.Combine(rootDir, @"Azure.Functions.Cli/func");
}
funcHostProcess.StartInfo.ArgumentList.Add("start");
if (enableAuth)
{
funcHostProcess.StartInfo.ArgumentList.Add("--enableAuth");
}

return funcHostProcess;
}

public static void StartProcessWithLogging(Process funcProcess)
{
funcProcess.ErrorDataReceived += (sender, e) => Console.WriteLine(e?.Data);
funcProcess.OutputDataReceived += (sender, e) => Console.WriteLine(e?.Data);

funcProcess.Start();

funcProcess.BeginErrorReadLine();
funcProcess.BeginOutputReadLine();
}

public static void KillExistingFuncHosts()
{
foreach (var func in Process.GetProcessesByName("func"))
{
func.Kill();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Diagnostics;
using System.Threading;
using Microsoft.Extensions.Logging;
using Xunit;

namespace Azure.Functions.Java.Tests.E2E
{
public class FunctionAppFixture : IDisposable
{
private readonly ILogger _logger;
private bool _disposed;
private Process _funcProcess;

public FunctionAppFixture()
{
// initialize logging
#pragma warning disable CS0618 // Type or member is obsolete
ILoggerFactory loggerFactory = new LoggerFactory().AddConsole();
#pragma warning restore CS0618 // Type or member is obsolete
_logger = loggerFactory.CreateLogger<FunctionAppFixture>();

// start host via CLI if testing locally
if (Constants.FunctionsHostUrl.Contains("localhost"))
{
// kill existing func processes
_logger.LogInformation("Shutting down any running functions hosts..");
FixtureHelpers.KillExistingFuncHosts();

// start functions process
_logger.LogInformation($"Starting functions host for {Constants.FunctionAppCollectionName}..");
_funcProcess = FixtureHelpers.GetFuncHostProcess();

FixtureHelpers.StartProcessWithLogging(_funcProcess);

_logger.LogInformation($"Waiting for functions host to be ready...");
Thread.Sleep(TimeSpan.FromSeconds(30));
}
}

protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
_logger.LogInformation("FunctionAppFixture disposing.");

if (_funcProcess != null)
{
_logger.LogInformation($"Shutting down functions host for {Constants.FunctionAppCollectionName}");
_funcProcess.Kill();
_funcProcess.Dispose();
}
}

_disposed = true;
}
}

public void Dispose()
{
Dispose(true);
}
}

[CollectionDefinition(Constants.FunctionAppCollectionName)]
public class FunctionAppCollection : ICollectionFixture<FunctionAppFixture>
{
// This class has no code, and is never created. Its purpose is simply
// to be the place to apply [CollectionDefinition] and all the
// ICollectionFixture<> interfaces.
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using Microsoft.WindowsAzure.Storage;
using Microsoft.WindowsAzure.Storage.Blob;
using Microsoft.WindowsAzure.Storage.Queue;
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;

namespace Azure.Functions.Java.Tests.E2E
{
class StorageHelpers
{
public static CloudStorageAccount _storageAccount = CloudStorageAccount.Parse(Constants.StorageConnectionStringSetting);
public static CloudQueueClient _queueClient = _storageAccount.CreateCloudQueueClient();
public static CloudBlobClient _cloudBlobClient = _storageAccount.CreateCloudBlobClient();

public async static Task DeleteQueue(string queueName)
{
CloudQueue queue = _queueClient.GetQueueReference(queueName);
await queue.DeleteAsync();
}

public async static Task ClearQueue(string queueName)
{
CloudQueue queue = _queueClient.GetQueueReference(queueName);
if (await queue.ExistsAsync())
{
await queue.ClearAsync();
}
}

public async static Task CreateQueue(string queueName)
{
CloudQueue queue = _queueClient.GetQueueReference(queueName);
await queue.CreateIfNotExistsAsync();
}

public async static Task<string> InsertIntoQueue(string queueName, string queueMessage)
{
CloudQueue queue = _queueClient.GetQueueReference(queueName);
await queue.CreateIfNotExistsAsync();
CloudQueueMessage message = new CloudQueueMessage(queueMessage);
await queue.AddMessageAsync(message);
return message.Id;
}

public async static Task<string> ReadFromQueue(string queueName)
{
CloudQueue queue = _queueClient.GetQueueReference(queueName);
CloudQueueMessage retrievedMessage = null;
await Utilities.RetryAsync(async () =>
{
retrievedMessage = await queue.GetMessageAsync();
return retrievedMessage != null;
}, pollingInterval: 4000);
await queue.DeleteMessageAsync(retrievedMessage);
return retrievedMessage.AsString;
}

public async static Task<IEnumerable<string>> ReadMessagesFromQueue(string queueName)
{
CloudQueue queue = _queueClient.GetQueueReference(queueName);
IEnumerable<CloudQueueMessage> retrievedMessages = null;
List<string> messages = new List<string>();
await Utilities.RetryAsync(async () =>
{
retrievedMessages = await queue.GetMessagesAsync(3);
return retrievedMessages != null;
});
foreach(CloudQueueMessage msg in retrievedMessages)
{
messages.Add(msg.AsString);
await queue.DeleteMessageAsync(msg);
}
return messages;
}


public async static Task ClearBlobContainers()
{
await ClearBlobContainer(Constants.TriggerInputBindingBlobContainer);
await ClearBlobContainer(Constants.InputBindingBlobContainer);
await ClearBlobContainer(Constants.OutputBindingBlobContainer);
}

public async static Task CreateBlobContainers()
{
await CreateBlobContainer(Constants.TriggerInputBindingBlobContainer);
await CreateBlobContainer(Constants.InputBindingBlobContainer);
await CreateBlobContainer(Constants.OutputBindingBlobContainer);
}

public async static Task UpdloadFileToContainer(string containerName, string expectedFileName)
{
string sourceFile = $"{expectedFileName}.txt";
File.WriteAllText(sourceFile, "Hello World");
CloudBlobContainer cloudBlobContainer = _cloudBlobClient.GetContainerReference(containerName);
CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(sourceFile);
await cloudBlockBlob.UploadFromFileAsync(sourceFile);
}

public async static Task<string> DownloadFileFromContainer(string containerName, string expectedFileName)
{
string destinationFile = $"{expectedFileName}_DOWNLOADED.txt";
string sourceFile = $"{expectedFileName}.txt";
CloudBlobContainer cloudBlobContainer = _cloudBlobClient.GetContainerReference(containerName);
CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference(sourceFile);
await Utilities.RetryAsync(async () =>
{
return await cloudBlockBlob.ExistsAsync();
}, pollingInterval: 4000, timeout: 120 * 1000);
await cloudBlockBlob.DownloadToFileAsync(destinationFile, FileMode.Create);
return File.ReadAllText(destinationFile);
}


private static async Task<CloudBlobContainer> CreateBlobContainer(string containerName)
{
BlobContainerPermissions permissions = new BlobContainerPermissions
{
PublicAccess = BlobContainerPublicAccessType.Blob
};
CloudBlobContainer cloudBlobContainer = _cloudBlobClient.GetContainerReference(containerName);
await cloudBlobContainer.CreateIfNotExistsAsync();
await cloudBlobContainer.SetPermissionsAsync(permissions);
return cloudBlobContainer;
}

private static async Task ClearBlobContainer(string containerName)
{
CloudBlobContainer cloudBlobContainer = _cloudBlobClient.GetContainerReference(containerName);
BlobContinuationToken blobContinuationToken = null;
do
{
if (!await cloudBlobContainer.ExistsAsync()) { continue; }
var results = await cloudBlobContainer.ListBlobsSegmentedAsync(null, blobContinuationToken);
// Get the value of the continuation token returned by the listing call.
blobContinuationToken = results.ContinuationToken;
foreach (IListBlobItem item in results.Results)
{
Console.WriteLine(item.Uri);
String blobName = System.IO.Path.GetFileName(item.Uri.AbsolutePath);
CloudBlob cloudBlob = cloudBlobContainer.GetBlobReference(blobName);
await cloudBlob.DeleteIfExistsAsync();
}
} while (blobContinuationToken != null); // Loop while the continuation token is not null.
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"profiles": {
"Azure.Functions.Java.Tests.E2E": {
"commandName": "Project"
}
}
}
Loading