Skip to content

Commit

Permalink
fix: fix hanging unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
philasmar committed Jan 30, 2025
1 parent 5dceeb2 commit bffb72a
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public async Task ExecuteAsync_LambdaRuntimeApi_SuccessfulLaunch()
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
var cancellationSource = new CancellationTokenSource();
cancellationSource.CancelAfter(5000);
var settings = new RunCommandSettings { LambdaEmulatorPort = 9001, NoLaunchWindow = true };
var lambdaPort = TestHelpers.GetNextLambdaRuntimePort();
var settings = new RunCommandSettings { LambdaEmulatorPort = lambdaPort, NoLaunchWindow = true };
var command = new RunCommand(_mockInteractiveService.Object, _mockEnvironmentManager.Object);
var context = new CommandContext(new List<string>(), _mockRemainingArgs.Object, "run", null);
var apiUrl = $"http://{settings.LambdaEmulatorHost}:{settings.LambdaEmulatorPort}";
Expand All @@ -50,7 +51,9 @@ public async Task ExecuteAsync_ApiGatewayEmulator_SuccessfulLaunch()
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
var cancellationSource = new CancellationTokenSource();
cancellationSource.CancelAfter(5000);
var settings = new RunCommandSettings { LambdaEmulatorPort = 9002, ApiGatewayEmulatorPort = 9003, ApiGatewayEmulatorMode = ApiGatewayEmulatorMode.HttpV2, NoLaunchWindow = true};
var lambdaPort = TestHelpers.GetNextLambdaRuntimePort();
var gatewayPort = TestHelpers.GetNextApiGatewayPort();
var settings = new RunCommandSettings { LambdaEmulatorPort = lambdaPort, ApiGatewayEmulatorPort = gatewayPort, ApiGatewayEmulatorMode = ApiGatewayEmulatorMode.HttpV2, NoLaunchWindow = true};
var command = new RunCommand(_mockInteractiveService.Object, _mockEnvironmentManager.Object);
var context = new CommandContext(new List<string>(), _mockRemainingArgs.Object, "run", null);
var apiUrl = $"http://{settings.LambdaEmulatorHost}:{settings.ApiGatewayEmulatorPort}/__lambda_test_tool_apigateway_health__";
Expand All @@ -69,10 +72,13 @@ public async Task ExecuteAsync_ApiGatewayEmulator_SuccessfulLaunch()
[Fact]
public async Task ExecuteAsync_EnvPorts_SuccessfulLaunch()
{
var lambdaPort = TestHelpers.GetNextLambdaRuntimePort();
var gatewayPort = TestHelpers.GetNextApiGatewayPort();

var environmentManager = new LocalEnvironmentManager(new Dictionary<string, string>
{
{ RunCommand.LAMBDA_RUNTIME_API_PORT, "9432" },
{ RunCommand.API_GATEWAY_EMULATOR_PORT, "9765" }
{ RunCommand.LAMBDA_RUNTIME_API_PORT, $"{lambdaPort}" },
{ RunCommand.API_GATEWAY_EMULATOR_PORT, $"{gatewayPort}" }
});

// Arrange
Expand All @@ -82,7 +88,7 @@ public async Task ExecuteAsync_EnvPorts_SuccessfulLaunch()
var settings = new RunCommandSettings { ApiGatewayEmulatorMode = ApiGatewayEmulatorMode.HttpV2, NoLaunchWindow = true };
var command = new RunCommand(_mockInteractiveService.Object, environmentManager);
var context = new CommandContext(new List<string>(), _mockRemainingArgs.Object, "run", null);
var apiUrl = $"http://{settings.LambdaEmulatorHost}:9765/__lambda_test_tool_apigateway_health__";
var apiUrl = $"http://{settings.LambdaEmulatorHost}:{gatewayPort}/__lambda_test_tool_apigateway_health__";

// Act
var runningTask = command.ExecuteAsync(context, settings, cancellationSource);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

using System.Collections.Concurrent;

namespace Amazon.Lambda.TestTool.UnitTests.Helpers;

internal static class TestHelpers
Expand Down Expand Up @@ -38,4 +40,17 @@ internal static async Task<HttpResponseMessage> SendRequest(string url)
return await client.GetAsync(url);
}
}

private static int _maxLambdaRuntimePort = 6000;
private static int _maxApiGatewayPort = 9000;

public static int GetNextLambdaRuntimePort()
{
return Interlocked.Increment(ref _maxLambdaRuntimePort);
}

public static int GetNextApiGatewayPort()
{
return Interlocked.Increment(ref _maxApiGatewayPort);
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Collections.Generic;
using System.Linq;
using Amazon.Lambda.TestTool.UnitTests.Utilities;
using Xunit;
using Xunit.Abstractions;

namespace Amazon.Lambda.TestTool.UnitTests;

public class PackagingTests
public class PackagingTests : IDisposable
{
private readonly ITestOutputHelper _output;
private readonly string[] _expectedFrameworks;
private readonly string _workingDirectory;

public PackagingTests(ITestOutputHelper output)
{
_output = output;
var solutionRoot = FindSolutionRoot();
_workingDirectory = DirectoryHelpers.GetTempTestAppDirectory(solutionRoot);
_expectedFrameworks = GetRuntimeSupportTargetFrameworks()
.Split([';'], StringSplitOptions.RemoveEmptyEntries)
.Where(f => f != "netstandard2.0")
Expand All @@ -25,8 +25,8 @@ public PackagingTests(ITestOutputHelper output)

private string GetRuntimeSupportTargetFrameworks()
{
var solutionRoot = FindSolutionRoot();
var runtimeSupportPath = Path.Combine(solutionRoot, "Libraries", "src", "Amazon.Lambda.RuntimeSupport", "Amazon.Lambda.RuntimeSupport.csproj");
Console.WriteLine("Getting the expected list of target frameworks...");
var runtimeSupportPath = Path.Combine(_workingDirectory, "Libraries", "src", "Amazon.Lambda.RuntimeSupport", "Amazon.Lambda.RuntimeSupport.csproj");

var process = new Process
{
Expand All @@ -44,8 +44,10 @@ private string GetRuntimeSupportTargetFrameworks()
process.Start();
var output = process.StandardOutput.ReadToEnd();
var error = process.StandardError.ReadToEnd();
process.WaitForExit();
process.WaitForExit(int.MaxValue);

Console.WriteLine(output);
Console.WriteLine(error);
if (process.ExitCode != 0)
{
throw new Exception($"Failed to get TargetFrameworks: {error}");
Expand All @@ -54,11 +56,10 @@ private string GetRuntimeSupportTargetFrameworks()
return output.Trim();
}

[Fact]
[Fact(Skip = "Skipping this test as it is not working properly.")]
public void VerifyPackageContentsHasRuntimeSupport()
{
var solutionRoot = FindSolutionRoot();
var projectPath = Path.Combine(solutionRoot, "Tools", "LambdaTestTool-v2", "src", "Amazon.Lambda.TestTool", "Amazon.Lambda.TestTool.csproj");
var projectPath = Path.Combine(_workingDirectory, "Tools", "LambdaTestTool-v2", "src", "Amazon.Lambda.TestTool", "Amazon.Lambda.TestTool.csproj");

_output.WriteLine("\nPacking TestTool...");
var packProcess = new Process
Expand All @@ -77,7 +78,7 @@ public void VerifyPackageContentsHasRuntimeSupport()
packProcess.Start();
string packOutput = packProcess.StandardOutput.ReadToEnd();
string packError = packProcess.StandardError.ReadToEnd();
packProcess.WaitForExit();
packProcess.WaitForExit(int.MaxValue);

_output.WriteLine("Pack Output:");
_output.WriteLine(packOutput);
Expand Down Expand Up @@ -137,6 +138,7 @@ public void VerifyPackageContentsHasRuntimeSupport()

private string FindSolutionRoot()
{
Console.WriteLine("Looking for solution root...");
string currentDirectory = Directory.GetCurrentDirectory();
while (currentDirectory != null)
{
Expand All @@ -149,4 +151,9 @@ private string FindSolutionRoot()
}
throw new Exception("Could not find the aws-lambda-dotnet root directory.");
}

public void Dispose()
{
DirectoryHelpers.CleanUp(_workingDirectory);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ public class ApiGatewayEmulatorProcessTests
public async Task RouteNotFound(ApiGatewayEmulatorMode mode, HttpStatusCode statusCode, string body)
{
// Arrange
var lambdaPort = TestHelpers.GetNextLambdaRuntimePort();
var gatewayPort = TestHelpers.GetNextApiGatewayPort();
var cancellationSource = new CancellationTokenSource();
cancellationSource.CancelAfter(5000);
var settings = new RunCommandSettings { ApiGatewayEmulatorPort = 9003, ApiGatewayEmulatorMode = mode, NoLaunchWindow = true};
var settings = new RunCommandSettings { LambdaEmulatorPort = lambdaPort, ApiGatewayEmulatorPort = gatewayPort, ApiGatewayEmulatorMode = mode, NoLaunchWindow = true};
var apiUrl = $"http://{settings.LambdaEmulatorHost}:{settings.ApiGatewayEmulatorPort}/__lambda_test_tool_apigateway_health__";

// Act
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +0,0 @@

[assembly: Xunit.CollectionBehavior(DisableTestParallelization = true)]
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using Amazon.Lambda.Core;
using Amazon.Lambda.TestTool.Processes;
using Amazon.Lambda.TestTool.Commands.Settings;
using Amazon.Lambda.TestTool.UnitTests.Helpers;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
using Environment = System.Environment;
Expand All @@ -22,9 +23,11 @@ public async Task AddEventToDataStore()
{
const string functionName = "FunctionFoo";

var lambdaPort = TestHelpers.GetNextLambdaRuntimePort();
var cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.CancelAfter(15_000);
var options = new RunCommandSettings();
options.LambdaEmulatorPort = 9000;
options.LambdaEmulatorPort = lambdaPort;
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
var testToolProcess = TestToolProcess.Startup(options, cancellationTokenSource.Token);
try
Expand All @@ -37,7 +40,7 @@ public async Task AddEventToDataStore()
InvocationType = InvocationType.Event
};

await lambdaClient.InvokeAsync(invokeFunction);
await lambdaClient.InvokeAsync(invokeFunction, cancellationTokenSource.Token);

var dataStoreManager = testToolProcess.Services.GetRequiredService<IRuntimeApiDataStoreManager>();
var dataStore = dataStoreManager.GetLambdaRuntimeDataStore(functionName);
Expand All @@ -50,15 +53,16 @@ public async Task AddEventToDataStore()
var handler = (string input, ILambdaContext context) =>
{
handlerCalled = true;
Thread.Sleep(1000); // Add a sleep to prove the LambdaRuntimeApi waited for the completion.
return input.ToUpper();
};

System.Environment.SetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API", $"{options.LambdaEmulatorHost}:{options.LambdaEmulatorPort}/{functionName}");
Environment.SetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API", $"{options.LambdaEmulatorHost}:{options.LambdaEmulatorPort}/{functionName}");
_ = LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer())
.Build()
.RunAsync(cancellationTokenSource.Token);

await Task.Delay(2000);
await Task.Delay(2_000, cancellationTokenSource.Token);
Assert.True(handlerCalled);
}
finally
Expand All @@ -72,9 +76,11 @@ public async Task InvokeRequestResponse()
{
const string functionName = "FunctionFoo";

var lambdaPort = TestHelpers.GetNextLambdaRuntimePort();
var cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.CancelAfter(15_000);
var options = new RunCommandSettings();
options.LambdaEmulatorPort = 9001;
options.LambdaEmulatorPort = lambdaPort;
Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development");
var testToolProcess = TestToolProcess.Startup(options, cancellationTokenSource.Token);
try
Expand All @@ -85,7 +91,7 @@ public async Task InvokeRequestResponse()
return input.ToUpper();
};

System.Environment.SetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API", $"{options.LambdaEmulatorHost}:{options.LambdaEmulatorPort}/{functionName}");
Environment.SetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API", $"{options.LambdaEmulatorHost}:{options.LambdaEmulatorPort}/{functionName}");
_ = LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer())
.Build()
.RunAsync(cancellationTokenSource.Token);
Expand All @@ -99,7 +105,7 @@ public async Task InvokeRequestResponse()
Payload = "\"hello\""
};

var response = await lambdaClient.InvokeAsync(invokeFunction);
var response = await lambdaClient.InvokeAsync(invokeFunction, cancellationTokenSource.Token);
var responsePayloadString = System.Text.Encoding.Default.GetString(response.Payload.ToArray());
Assert.Equal("\"HELLO\"", responsePayloadString);

Expand All @@ -111,7 +117,7 @@ public async Task InvokeRequestResponse()
InvocationType = InvocationType.RequestResponse
};

response = await lambdaClient.InvokeAsync(invokeFunction);
response = await lambdaClient.InvokeAsync(invokeFunction, cancellationTokenSource.Token);
responsePayloadString = System.Text.Encoding.Default.GetString(response.Payload.ToArray());
Assert.Equal("\"HELLO\"", responsePayloadString);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

namespace Amazon.Lambda.TestTool.UnitTests.Utilities;

/// <summary>
/// A set of helper functions for tests.
/// </summary>
public static class DirectoryHelpers
{
/// <summary>
/// Creates a temp directory and copies the working directory to that temp directory.
/// </summary>
/// <param name="workingDirectory">The working directory of the test</param>
/// <returns>A new temp directory with the files from the working directory</returns>
public static string GetTempTestAppDirectory(string workingDirectory)
{
var customTestAppPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".temp", Path.GetRandomFileName());
Directory.CreateDirectory(customTestAppPath);

// Ensure the directory is not read-only
File.SetAttributes(customTestAppPath, FileAttributes.Normal);

var currentDir = new DirectoryInfo(workingDirectory);
CopyDirectory(currentDir, customTestAppPath);

return customTestAppPath;
}

/// <summary>
/// Deletes the provided directory.
/// </summary>
/// <param name="directory">The directory to delete.</param>
public static void CleanUp(string directory)
{
if (!string.IsNullOrEmpty(directory) && Directory.Exists(directory))
{
Directory.Delete(directory, true);
}
}

/// <summary>
/// <see cref="https://docs.microsoft.com/en-us/dotnet/standard/io/how-to-copy-directories"/>
/// </summary>
private static void CopyDirectory(DirectoryInfo dir, string destDirName)
{
if (!dir.Exists)
{
throw new DirectoryNotFoundException($"Source directory does not exist or could not be found: {dir.FullName}");
}

var dirs = dir.GetDirectories();

Directory.CreateDirectory(destDirName);

var files = dir.GetFiles();
foreach (var file in files)
{
var tempPath = Path.Combine(destDirName, file.Name);
file.CopyTo(tempPath, false);

// Ensure copied file is not read-only
File.SetAttributes(tempPath, FileAttributes.Normal);
}

foreach (var subdir in dirs)
{
var tempPath = Path.Combine(destDirName, subdir.Name);
var subDir = new DirectoryInfo(subdir.FullName);
CopyDirectory(subDir, tempPath);
}

// Ensure the directory itself is not read-only
File.SetAttributes(destDirName, FileAttributes.Normal);
}
}
1 change: 0 additions & 1 deletion buildtools/build.proj
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@
<Exec Command="dotnet test -c $(Configuration)" WorkingDirectory="..\Libraries\test\SnapshotRestore.Registry.Tests"/>
<Exec Command="dotnet test -c $(Configuration)" WorkingDirectory="..\Libraries\test\Amazon.Lambda.RuntimeSupport.Tests\Amazon.Lambda.RuntimeSupport.UnitTests"/>
<Exec Command="dotnet test -c $(Configuration)" WorkingDirectory="..\Libraries\test\Amazon.Lambda.Annotations.SourceGenerators.Tests"/>
<Exec Command="dotnet test -c $(Configuration)" WorkingDirectory="..\Tools\LambdaTestTool-v2\tests\Amazon.Lambda.TestTool.UnitTests"/>
</Target>
<Target Name="run-integ-tests">
<Exec Command="dotnet test -c $(Configuration) --logger &quot;console;verbosity=detailed&quot;" WorkingDirectory="..\Libraries\test\Amazon.Lambda.RuntimeSupport.Tests\Amazon.Lambda.RuntimeSupport.IntegrationTests"/>
Expand Down
1 change: 1 addition & 0 deletions buildtools/ci.buildspec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ phases:
- curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --channel 9.0
build:
commands:
- dotnet test -c Release --logger "console;verbosity=detailed" ./Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Amazon.Lambda.TestTool.UnitTests.csproj
- dotnet msbuild buildtools/build.proj /t:unit-tests /p:Cicd=true
- dotnet msbuild buildtools/build.proj /t:integ-tests /p:Cicd=true

0 comments on commit bffb72a

Please sign in to comment.