diff --git a/LambdaRuntimeDockerfiles/Images/net6/amd64/Dockerfile b/LambdaRuntimeDockerfiles/Images/net6/amd64/Dockerfile index 2e552da90..3d317c5e3 100644 --- a/LambdaRuntimeDockerfiles/Images/net6/amd64/Dockerfile +++ b/LambdaRuntimeDockerfiles/Images/net6/amd64/Dockerfile @@ -18,7 +18,7 @@ WORKDIR /dotnet RUN yum install tar gzip --assumeyes # Install the ASP.NET Core shared framework -RUN curl -SL --output aspnetcore.tar.gz https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$ASPNET_VERSION/aspnetcore-runtime-$ASPNET_VERSION-linux-x64.tar.gz \ +RUN curl -SL --output aspnetcore.tar.gz https://builds.dotnet.microsoft.com/dotnet/aspnetcore/Runtime/$ASPNET_VERSION/aspnetcore-runtime-$ASPNET_VERSION-linux-x64.tar.gz \ && aspnetcore_sha512=$ASPNET_SHA512 \ && echo "$aspnetcore_sha512 aspnetcore.tar.gz" | sha512sum -c - \ && tar -ozxf aspnetcore.tar.gz -C /dotnet \ diff --git a/LambdaRuntimeDockerfiles/Images/net6/arm64/Dockerfile b/LambdaRuntimeDockerfiles/Images/net6/arm64/Dockerfile index f7464267c..2f7db39e3 100644 --- a/LambdaRuntimeDockerfiles/Images/net6/arm64/Dockerfile +++ b/LambdaRuntimeDockerfiles/Images/net6/arm64/Dockerfile @@ -48,7 +48,7 @@ WORKDIR /dotnet RUN yum install tar gzip --assumeyes # Install the ASP.NET Core shared framework -RUN curl -SL --output aspnetcore.tar.gz https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$ASPNET_VERSION/aspnetcore-runtime-$ASPNET_VERSION-linux-arm64.tar.gz \ +RUN curl -SL --output aspnetcore.tar.gz https://builds.dotnet.microsoft.com/dotnet/aspnetcore/Runtime/$ASPNET_VERSION/aspnetcore-runtime-$ASPNET_VERSION-linux-arm64.tar.gz \ && aspnetcore_sha512=$ASPNET_SHA512 \ && echo "$aspnetcore_sha512 aspnetcore.tar.gz" | sha512sum -c - \ && tar -ozxf aspnetcore.tar.gz -C /dotnet \ diff --git a/LambdaRuntimeDockerfiles/Images/net8/amd64/Dockerfile b/LambdaRuntimeDockerfiles/Images/net8/amd64/Dockerfile index b6e283d05..fd606b142 100644 --- a/LambdaRuntimeDockerfiles/Images/net8/amd64/Dockerfile +++ b/LambdaRuntimeDockerfiles/Images/net8/amd64/Dockerfile @@ -20,7 +20,7 @@ WORKDIR /dotnet RUN dnf install tar gzip --assumeyes # Install the ASP.NET Core shared framework -RUN curl -SL --output aspnetcore.tar.gz https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$ASPNET_VERSION/aspnetcore-runtime-$ASPNET_VERSION-linux-x64.tar.gz \ +RUN curl -SL --output aspnetcore.tar.gz https://builds.dotnet.microsoft.com/dotnet/aspnetcore/Runtime/$ASPNET_VERSION/aspnetcore-runtime-$ASPNET_VERSION-linux-x64.tar.gz \ && aspnetcore_sha512=$ASPNET_SHA512 \ && echo "$aspnetcore_sha512 aspnetcore.tar.gz" | sha512sum -c - \ && tar -ozxf aspnetcore.tar.gz -C /dotnet \ diff --git a/LambdaRuntimeDockerfiles/Images/net8/arm64/Dockerfile b/LambdaRuntimeDockerfiles/Images/net8/arm64/Dockerfile index 17e4131e1..6e21e15ac 100644 --- a/LambdaRuntimeDockerfiles/Images/net8/arm64/Dockerfile +++ b/LambdaRuntimeDockerfiles/Images/net8/arm64/Dockerfile @@ -20,7 +20,7 @@ WORKDIR /dotnet RUN dnf install tar gzip --assumeyes # Install the ASP.NET Core shared framework -RUN curl -SL --output aspnetcore.tar.gz https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$ASPNET_VERSION/aspnetcore-runtime-$ASPNET_VERSION-linux-arm64.tar.gz \ +RUN curl -SL --output aspnetcore.tar.gz https://builds.dotnet.microsoft.com/dotnet/aspnetcore/Runtime/$ASPNET_VERSION/aspnetcore-runtime-$ASPNET_VERSION-linux-arm64.tar.gz \ && aspnetcore_sha512=$ASPNET_SHA512 \ && echo "$aspnetcore_sha512 aspnetcore.tar.gz" | sha512sum -c - \ && tar -ozxf aspnetcore.tar.gz -C /dotnet \ diff --git a/LambdaRuntimeDockerfiles/Images/net9/amd64/Dockerfile b/LambdaRuntimeDockerfiles/Images/net9/amd64/Dockerfile index 4db7e77f3..a4f33f45b 100644 --- a/LambdaRuntimeDockerfiles/Images/net9/amd64/Dockerfile +++ b/LambdaRuntimeDockerfiles/Images/net9/amd64/Dockerfile @@ -20,7 +20,7 @@ WORKDIR /dotnet RUN dnf install tar gzip --assumeyes # Install the ASP.NET Core shared framework -RUN curl -SL --output aspnetcore.tar.gz https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$ASPNET_VERSION/aspnetcore-runtime-$ASPNET_VERSION-linux-x64.tar.gz \ +RUN curl -SL --output aspnetcore.tar.gz https://builds.dotnet.microsoft.com/dotnet/aspnetcore/Runtime/$ASPNET_VERSION/aspnetcore-runtime-$ASPNET_VERSION-linux-x64.tar.gz \ && aspnetcore_sha512=$ASPNET_SHA512 \ && echo "$aspnetcore_sha512 aspnetcore.tar.gz" | sha512sum -c - \ && tar -ozxf aspnetcore.tar.gz -C /dotnet \ diff --git a/LambdaRuntimeDockerfiles/Images/net9/arm64/Dockerfile b/LambdaRuntimeDockerfiles/Images/net9/arm64/Dockerfile index a1af81dd7..e37e53352 100644 --- a/LambdaRuntimeDockerfiles/Images/net9/arm64/Dockerfile +++ b/LambdaRuntimeDockerfiles/Images/net9/arm64/Dockerfile @@ -20,7 +20,7 @@ WORKDIR /dotnet RUN dnf install tar gzip --assumeyes # Install the ASP.NET Core shared framework -RUN curl -SL --output aspnetcore.tar.gz https://dotnetcli.azureedge.net/dotnet/aspnetcore/Runtime/$ASPNET_VERSION/aspnetcore-runtime-$ASPNET_VERSION-linux-arm64.tar.gz \ +RUN curl -SL --output aspnetcore.tar.gz https://builds.dotnet.microsoft.com/dotnet/aspnetcore/Runtime/$ASPNET_VERSION/aspnetcore-runtime-$ASPNET_VERSION-linux-arm64.tar.gz \ && aspnetcore_sha512=$ASPNET_SHA512 \ && echo "$aspnetcore_sha512 aspnetcore.tar.gz" | sha512sum -c - \ && tar -ozxf aspnetcore.tar.gz -C /dotnet \ diff --git a/Libraries/src/Amazon.Lambda.RuntimeSupport/Amazon.Lambda.RuntimeSupport.csproj b/Libraries/src/Amazon.Lambda.RuntimeSupport/Amazon.Lambda.RuntimeSupport.csproj index d052fce7b..04405a680 100644 --- a/Libraries/src/Amazon.Lambda.RuntimeSupport/Amazon.Lambda.RuntimeSupport.csproj +++ b/Libraries/src/Amazon.Lambda.RuntimeSupport/Amazon.Lambda.RuntimeSupport.csproj @@ -18,6 +18,7 @@ Exe + $(DefineConstants);ExecutableOutputType diff --git a/Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/InvokeDelegateBuilder.cs b/Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/InvokeDelegateBuilder.cs index cab84397f..0cbbe1829 100644 --- a/Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/InvokeDelegateBuilder.cs +++ b/Libraries/src/Amazon.Lambda.RuntimeSupport/Bootstrap/InvokeDelegateBuilder.cs @@ -29,6 +29,9 @@ namespace Amazon.Lambda.RuntimeSupport.Bootstrap /// /// Builds user delegate from the handler information. /// +#if NET8_0_OR_GREATER + [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("InvokeDelegateBuilder does not support trimming and is meant to be used in class library based Lambda functions.")] +#endif internal class InvokeDelegateBuilder { private readonly InternalLogger _logger; diff --git a/Libraries/src/Amazon.Lambda.RuntimeSupport/Program.cs b/Libraries/src/Amazon.Lambda.RuntimeSupport/Program.cs index b3c7f8d91..8f87eb354 100644 --- a/Libraries/src/Amazon.Lambda.RuntimeSupport/Program.cs +++ b/Libraries/src/Amazon.Lambda.RuntimeSupport/Program.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). @@ -23,6 +23,8 @@ namespace Amazon.Lambda.RuntimeSupport { class Program { + +#if ExecutableOutputType #if NET8_0_OR_GREATER [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode( "The Main entry point is used in the managed runtime which loads Lambda functions as a class library. " + @@ -42,6 +44,7 @@ private static async Task Main(string[] args) RuntimeSupportInitializer runtimeSupportInitializer = new RuntimeSupportInitializer(handler); await runtimeSupportInitializer.RunLambdaBootstrap(); } +#endif #if NET8_0_OR_GREATER [System.Diagnostics.CodeAnalysis.RequiresUnreferencedCode("This code is only exercised in the class library programming model. Native AOT will not use this code path.")] diff --git a/Tools/LambdaTestTool-v2/Amazon.Lambda.TestTool.sln b/Tools/LambdaTestTool-v2/Amazon.Lambda.TestTool.sln index da704cc02..e330a0ce2 100644 --- a/Tools/LambdaTestTool-v2/Amazon.Lambda.TestTool.sln +++ b/Tools/LambdaTestTool-v2/Amazon.Lambda.TestTool.sln @@ -6,21 +6,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.Lambda.TestTool", "s EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{0AB3BF05-4346-4AA6-1389-037BE0695223}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.Lambda.TestTool.Tests.Common", "tests\Amazon.Lambda.TestTool.Tests.Common\Amazon.Lambda.TestTool.Tests.Common.csproj", "{E4D6D10C-C65F-E5E7-F865-FA931477FBCC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.Lambda.TestTool.UnitTests", "tests\Amazon.Lambda.TestTool.UnitTests\Amazon.Lambda.TestTool.UnitTests.csproj", "{80A4F809-28B7-61EC-6539-DF3C7A0733FD}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.Lambda.TestTool.IntegrationTests", "tests\Amazon.Lambda.TestTool.IntegrationTests\Amazon.Lambda.TestTool.IntegrationTests.csproj", "{F7B6DF0E-EEB2-4B3F-47B7-49B188A2A216}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LambdaTestFunctionV2", "testapps\LambdaTestFunctionV2\LambdaTestFunctionV2.csproj", "{803C76E6-8804-A4DF-8896-DDBC9FBEE8DD}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LambdaBinaryFunction", "testapps\LambdaBinaryFunction\LambdaBinaryFunction.csproj", "{F625BA55-B999-6F5D-4DA1-9D4C77996D6A}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LambdaReturnStringFunction", "testapps\LambdaReturnStringFunction\LambdaReturnStringFunction.csproj", "{54F64435-2082-3F4E-D3A8-B90BE58EF2EB}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LambdaTestFunctionV1", "testapps\LambdaTestFunctionV1\LambdaTestFunctionV1.csproj", "{B532EEC5-2AA9-88BA-8D0B-46ECC392791A}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.Lambda.TestTool.Tests.Common", "tests\Amazon.Lambda.TestTool.Tests.Common\Amazon.Lambda.TestTool.Tests.Common.csproj", "{E4D6D10C-C65F-E5E7-F865-FA931477FBCC}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "testapps", "testapps", "{4553EF99-1D3C-14C7-0D22-5364D18C373B}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Amazon.Lambda.TestTool.UnitTests", "tests\Amazon.Lambda.TestTool.UnitTests\Amazon.Lambda.TestTool.UnitTests.csproj", "{80A4F809-28B7-61EC-6539-DF3C7A0733FD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -32,6 +22,10 @@ Global {97EE2E8A-D1F4-CB11-B664-B99B036E9F7B}.Debug|Any CPU.Build.0 = Debug|Any CPU {97EE2E8A-D1F4-CB11-B664-B99B036E9F7B}.Release|Any CPU.ActiveCfg = Release|Any CPU {97EE2E8A-D1F4-CB11-B664-B99B036E9F7B}.Release|Any CPU.Build.0 = Release|Any CPU + {F7B6DF0E-EEB2-4B3F-47B7-49B188A2A216}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F7B6DF0E-EEB2-4B3F-47B7-49B188A2A216}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F7B6DF0E-EEB2-4B3F-47B7-49B188A2A216}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F7B6DF0E-EEB2-4B3F-47B7-49B188A2A216}.Release|Any CPU.Build.0 = Release|Any CPU {E4D6D10C-C65F-E5E7-F865-FA931477FBCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E4D6D10C-C65F-E5E7-F865-FA931477FBCC}.Debug|Any CPU.Build.0 = Debug|Any CPU {E4D6D10C-C65F-E5E7-F865-FA931477FBCC}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -40,33 +34,9 @@ Global {80A4F809-28B7-61EC-6539-DF3C7A0733FD}.Debug|Any CPU.Build.0 = Debug|Any CPU {80A4F809-28B7-61EC-6539-DF3C7A0733FD}.Release|Any CPU.ActiveCfg = Release|Any CPU {80A4F809-28B7-61EC-6539-DF3C7A0733FD}.Release|Any CPU.Build.0 = Release|Any CPU - {F7B6DF0E-EEB2-4B3F-47B7-49B188A2A216}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F7B6DF0E-EEB2-4B3F-47B7-49B188A2A216}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F7B6DF0E-EEB2-4B3F-47B7-49B188A2A216}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F7B6DF0E-EEB2-4B3F-47B7-49B188A2A216}.Release|Any CPU.Build.0 = Release|Any CPU - {803C76E6-8804-A4DF-8896-DDBC9FBEE8DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {803C76E6-8804-A4DF-8896-DDBC9FBEE8DD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {803C76E6-8804-A4DF-8896-DDBC9FBEE8DD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {803C76E6-8804-A4DF-8896-DDBC9FBEE8DD}.Release|Any CPU.Build.0 = Release|Any CPU - {F625BA55-B999-6F5D-4DA1-9D4C77996D6A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F625BA55-B999-6F5D-4DA1-9D4C77996D6A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F625BA55-B999-6F5D-4DA1-9D4C77996D6A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F625BA55-B999-6F5D-4DA1-9D4C77996D6A}.Release|Any CPU.Build.0 = Release|Any CPU - {54F64435-2082-3F4E-D3A8-B90BE58EF2EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {54F64435-2082-3F4E-D3A8-B90BE58EF2EB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {54F64435-2082-3F4E-D3A8-B90BE58EF2EB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {54F64435-2082-3F4E-D3A8-B90BE58EF2EB}.Release|Any CPU.Build.0 = Release|Any CPU - {B532EEC5-2AA9-88BA-8D0B-46ECC392791A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B532EEC5-2AA9-88BA-8D0B-46ECC392791A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B532EEC5-2AA9-88BA-8D0B-46ECC392791A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B532EEC5-2AA9-88BA-8D0B-46ECC392791A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {97EE2E8A-D1F4-CB11-B664-B99B036E9F7B} = {827E0CD3-B72D-47B6-A68D-7590B98EB39B} - {F625BA55-B999-6F5D-4DA1-9D4C77996D6A} = {4553EF99-1D3C-14C7-0D22-5364D18C373B} - {54F64435-2082-3F4E-D3A8-B90BE58EF2EB} = {4553EF99-1D3C-14C7-0D22-5364D18C373B} - {B532EEC5-2AA9-88BA-8D0B-46ECC392791A} = {4553EF99-1D3C-14C7-0D22-5364D18C373B} - {803C76E6-8804-A4DF-8896-DDBC9FBEE8DD} = {4553EF99-1D3C-14C7-0D22-5364D18C373B} {F7B6DF0E-EEB2-4B3F-47B7-49B188A2A216} = {0AB3BF05-4346-4AA6-1389-037BE0695223} {E4D6D10C-C65F-E5E7-F865-FA931477FBCC} = {0AB3BF05-4346-4AA6-1389-037BE0695223} {80A4F809-28B7-61EC-6539-DF3C7A0733FD} = {0AB3BF05-4346-4AA6-1389-037BE0695223} diff --git a/Tools/LambdaTestTool-v2/Amazon.Lambda.TestTool.slnx b/Tools/LambdaTestTool-v2/Amazon.Lambda.TestTool.slnx index 5db12de26..825c06bd2 100644 --- a/Tools/LambdaTestTool-v2/Amazon.Lambda.TestTool.slnx +++ b/Tools/LambdaTestTool-v2/Amazon.Lambda.TestTool.slnx @@ -2,12 +2,6 @@ - - - - - - diff --git a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Amazon.Lambda.TestTool.csproj b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Amazon.Lambda.TestTool.csproj index 697bab855..269a796ce 100644 --- a/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Amazon.Lambda.TestTool.csproj +++ b/Tools/LambdaTestTool-v2/src/Amazon.Lambda.TestTool/Amazon.Lambda.TestTool.csproj @@ -1,4 +1,4 @@ - + A tool to help debug and test your .NET AWS Lambda functions locally. @@ -34,17 +34,18 @@ - - - + - + + + + true diff --git a/Tools/LambdaTestTool-v2/testapps/LambdaBinaryFunction/Function.cs b/Tools/LambdaTestTool-v2/testapps/LambdaBinaryFunction/Function.cs deleted file mode 100644 index 8aa5a121b..000000000 --- a/Tools/LambdaTestTool-v2/testapps/LambdaBinaryFunction/Function.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; -using Amazon.Lambda.RuntimeSupport; -using Amazon.Lambda.Serialization.SystemTextJson; - -var GenerateBinary = (APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) => -{ - // Create a simple binary pattern (for example, counting bytes from 0 to 255) - byte[] binaryData = new byte[256]; - for (int i = 0; i < 256; i++) - { - binaryData[i] = (byte)i; - } - - return new APIGatewayHttpApiV2ProxyResponse - { - StatusCode = 200, - Body = Convert.ToBase64String(binaryData), - IsBase64Encoded = true, - Headers = new Dictionary - { - { "Content-Type", "application/octet-stream" } - } - }; -}; - -await LambdaBootstrapBuilder.Create(GenerateBinary, new CamelCaseLambdaJsonSerializer()) - .Build() - .RunAsync(); diff --git a/Tools/LambdaTestTool-v2/testapps/LambdaBinaryFunction/LambdaBinaryFunction.csproj b/Tools/LambdaTestTool-v2/testapps/LambdaBinaryFunction/LambdaBinaryFunction.csproj deleted file mode 100644 index 3fc64eafd..000000000 --- a/Tools/LambdaTestTool-v2/testapps/LambdaBinaryFunction/LambdaBinaryFunction.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - Exe - net8.0 - enable - enable - true - Lambda - - true - - true - - - - - - - - \ No newline at end of file diff --git a/Tools/LambdaTestTool-v2/testapps/LambdaBinaryFunction/aws-lambda-tools-defaults.json b/Tools/LambdaTestTool-v2/testapps/LambdaBinaryFunction/aws-lambda-tools-defaults.json deleted file mode 100644 index f852b483b..000000000 --- a/Tools/LambdaTestTool-v2/testapps/LambdaBinaryFunction/aws-lambda-tools-defaults.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "Information": [ - "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", - "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", - "dotnet lambda help", - "All the command line options for the Lambda command can be specified in this file." - ], - "profile": "", - "region": "", - "configuration": "Release", - "function-runtime": "dotnet8", - "function-memory-size": 512, - "function-timeout": 30, - "function-handler": "LambdaBinaryFunction" -} \ No newline at end of file diff --git a/Tools/LambdaTestTool-v2/testapps/LambdaReturnStringFunction/Function.cs b/Tools/LambdaTestTool-v2/testapps/LambdaReturnStringFunction/Function.cs deleted file mode 100644 index 4b00804e4..000000000 --- a/Tools/LambdaTestTool-v2/testapps/LambdaReturnStringFunction/Function.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; -using Amazon.Lambda.RuntimeSupport; -using Amazon.Lambda.Serialization.SystemTextJson; - -var ToUpper = (APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) => -{ - return request.Body.ToUpper(); -}; - -await LambdaBootstrapBuilder.Create(ToUpper, new CamelCaseLambdaJsonSerializer()) - .Build() - .RunAsync(); diff --git a/Tools/LambdaTestTool-v2/testapps/LambdaReturnStringFunction/LambdaReturnStringFunction.csproj b/Tools/LambdaTestTool-v2/testapps/LambdaReturnStringFunction/LambdaReturnStringFunction.csproj deleted file mode 100644 index ca9ce679b..000000000 --- a/Tools/LambdaTestTool-v2/testapps/LambdaReturnStringFunction/LambdaReturnStringFunction.csproj +++ /dev/null @@ -1,21 +0,0 @@ - - - Exe - net8.0 - enable - enable - true - Lambda - - true - - true - LambdaReturnString - - - - - - - - \ No newline at end of file diff --git a/Tools/LambdaTestTool-v2/testapps/LambdaReturnStringFunction/aws-lambda-tools-defaults.json b/Tools/LambdaTestTool-v2/testapps/LambdaReturnStringFunction/aws-lambda-tools-defaults.json deleted file mode 100644 index 188e71395..000000000 --- a/Tools/LambdaTestTool-v2/testapps/LambdaReturnStringFunction/aws-lambda-tools-defaults.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "Information": [ - "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", - "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", - "dotnet lambda help", - "All the command line options for the Lambda command can be specified in this file." - ], - "profile": "", - "region": "", - "configuration": "Release", - "function-runtime": "dotnet8", - "function-memory-size": 512, - "function-timeout": 30, - "function-handler": "ToUpper" -} diff --git a/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV1/Function.cs b/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV1/Function.cs deleted file mode 100644 index 380a2e70c..000000000 --- a/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV1/Function.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; -using Amazon.Lambda.RuntimeSupport; -using Amazon.Lambda.Serialization.SystemTextJson; - -var ToUpper = (APIGatewayProxyRequest request, ILambdaContext context) => -{ - return new APIGatewayProxyResponse() - { - StatusCode = 200, - Body = request.Body.ToUpper(), - IsBase64Encoded = false, - }; -}; - -await LambdaBootstrapBuilder.Create(ToUpper, new CamelCaseLambdaJsonSerializer()) - .Build() - .RunAsync(); diff --git a/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV1/LambdaTestFunctionV1.csproj b/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV1/LambdaTestFunctionV1.csproj deleted file mode 100644 index 3fc64eafd..000000000 --- a/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV1/LambdaTestFunctionV1.csproj +++ /dev/null @@ -1,20 +0,0 @@ - - - Exe - net8.0 - enable - enable - true - Lambda - - true - - true - - - - - - - - \ No newline at end of file diff --git a/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV1/aws-lambda-tools-defaults.json b/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV1/aws-lambda-tools-defaults.json deleted file mode 100644 index d7c2eb348..000000000 --- a/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV1/aws-lambda-tools-defaults.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "Information": [ - "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", - "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", - "dotnet lambda help", - "All the command line options for the Lambda command can be specified in this file." - ], - "profile": "", - "region": "", - "configuration": "Release", - "function-runtime": "dotnet8", - "function-memory-size": 512, - "function-timeout": 30, - "function-handler": "LambdaTestFunctionV1" -} \ No newline at end of file diff --git a/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV2/Function.cs b/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV2/Function.cs deleted file mode 100644 index 835fd0e8a..000000000 --- a/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV2/Function.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Amazon.Lambda.APIGatewayEvents; -using Amazon.Lambda.Core; -using Amazon.Lambda.RuntimeSupport; -using Amazon.Lambda.Serialization.SystemTextJson; - -var ToUpper = (APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) => -{ - return new APIGatewayHttpApiV2ProxyResponse - { - StatusCode = 200, - Body = request.Body.ToUpper() - }; -}; - -await LambdaBootstrapBuilder.Create(ToUpper, new CamelCaseLambdaJsonSerializer()) - .Build() - .RunAsync(); diff --git a/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV2/LambdaTestFunctionV2.csproj b/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV2/LambdaTestFunctionV2.csproj deleted file mode 100644 index b35be1ec5..000000000 --- a/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV2/LambdaTestFunctionV2.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - net8.0 - enable - enable - true - Lambda - - true - - true - Exe - LambdaTestFunction - - - - - - - - - - - \ No newline at end of file diff --git a/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV2/aws-lambda-tools-defaults.json b/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV2/aws-lambda-tools-defaults.json deleted file mode 100644 index 55632b432..000000000 --- a/Tools/LambdaTestTool-v2/testapps/LambdaTestFunctionV2/aws-lambda-tools-defaults.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "Information": [ - "This file provides default values for the deployment wizard inside Visual Studio and the AWS Lambda commands added to the .NET Core CLI.", - "To learn more about the Lambda commands with the .NET Core CLI execute the following command at the command line in the project root directory.", - "dotnet lambda help", - "All the command line options for the Lambda command can be specified in this file." - ], - "profile": "default", - "region": "us-west-2", - "configuration": "Release", - "function-runtime": "dotnet8", - "function-memory-size": 512, - "function-timeout": 30, - "function-handler": "ToUpper" -} diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.IntegrationTests/Amazon.Lambda.TestTool.IntegrationTests.csproj b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.IntegrationTests/Amazon.Lambda.TestTool.IntegrationTests.csproj index bd9f4df17..00f962109 100644 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.IntegrationTests/Amazon.Lambda.TestTool.IntegrationTests.csproj +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.IntegrationTests/Amazon.Lambda.TestTool.IntegrationTests.csproj @@ -10,6 +10,9 @@ + + + diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.IntegrationTests/ApiGatewayEmulatorProcessTests.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.IntegrationTests/ApiGatewayEmulatorProcessTests.cs index a31dde0fb..275d28f6a 100644 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.IntegrationTests/ApiGatewayEmulatorProcessTests.cs +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.IntegrationTests/ApiGatewayEmulatorProcessTests.cs @@ -1,11 +1,13 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -using System.Collections.Concurrent; -using System.Diagnostics; using System.Net; using System.Net.Sockets; using System.Text; +using Amazon.Lambda.APIGatewayEvents; +using Amazon.Lambda.Core; +using Amazon.Lambda.RuntimeSupport; +using Amazon.Lambda.Serialization.SystemTextJson; using Amazon.Lambda.TestTool.Commands; using Amazon.Lambda.TestTool.Commands.Settings; using Amazon.Lambda.TestTool.Models; @@ -15,52 +17,46 @@ using Spectre.Console.Cli; using Xunit; using Xunit.Abstractions; +using Amazon.Lambda.TestTool.Tests.Common.Retries; namespace Amazon.Lambda.TestTool.IntegrationTests; -public class ApiGatewayEmulatorProcessTests : IAsyncDisposable +public class ApiGatewayEmulatorProcessTests(ITestOutputHelper testOutputHelper) { - private readonly Mock _mockEnvironmentManager = new Mock(); - private readonly Mock _mockInteractiveService = new Mock(); - private readonly Mock _mockRemainingArgs = new Mock(); - private readonly ITestOutputHelper _testOutputHelper; - private readonly ConcurrentQueue _logMessages = new ConcurrentQueue(); - private readonly SemaphoreSlim _logSemaphore = new SemaphoreSlim(1, 1); - private Process? _lambdaProcess; - - public ApiGatewayEmulatorProcessTests(ITestOutputHelper testOutputHelper) - { - _testOutputHelper = testOutputHelper; - } + private readonly Mock _mockEnvironmentManager = new(); + private readonly Mock _mockInteractiveService = new(); + private readonly Mock _mockRemainingArgs = new(); + private CancellationTokenSource _cancellationTokenSource = new(); -#if DEBUG - [Fact] -#else - [Fact(Skip = "Skipping this test as it is not working properly.")] -#endif + [RetryFact] public async Task TestLambdaToUpperV2() { var (lambdaPort, apiGatewayPort) = await GetFreePorts(); - - var testProjectDir = Path.GetFullPath("../../../../../testapps"); - var config = new TestConfig - { - TestToolPath = Path.GetFullPath(Path.Combine(testProjectDir, "../src/Amazon.Lambda.TestTool")), - LambdaPath = Path.GetFullPath(Path.Combine(testProjectDir, "LambdaTestFunctionV2")), - FunctionName = "LambdaTestFunctionV2", - RouteName = "testfunction", - HttpMethod = "Post" - }; - - var cancellationTokenSource = new CancellationTokenSource(); - + _cancellationTokenSource = new CancellationTokenSource(); + _cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(120)); + var consoleError = Console.Error; try { - StartTestToolProcess(ApiGatewayEmulatorMode.HttpV2, config, lambdaPort, apiGatewayPort, cancellationTokenSource); + Console.SetError(TextWriter.Null); + await StartTestToolProcessAsync(ApiGatewayEmulatorMode.HttpV2, "testfunction", lambdaPort, apiGatewayPort, _cancellationTokenSource); await WaitForGatewayHealthCheck(apiGatewayPort); - await StartLambdaProcess(config, lambdaPort); + var handler = (APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) => + { + var env = Environment.GetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API"); + testOutputHelper.WriteLine($"TestLambdaToUpperV2: {env}"); + return new APIGatewayHttpApiV2ProxyResponse + { + StatusCode = 200, + Body = request.Body.ToUpper() + }; + }; + + Environment.SetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API", $"localhost:{lambdaPort}/testfunction"); + _ = LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer()) + .Build() + .RunAsync(_cancellationTokenSource.Token); - var response = await TestEndpoint(config, apiGatewayPort); + var response = await TestEndpoint("testfunction", apiGatewayPort); var responseContent = await response.Content.ReadAsStringAsync(); Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -68,38 +64,41 @@ public async Task TestLambdaToUpperV2() } finally { - await cancellationTokenSource.CancelAsync(); + await _cancellationTokenSource.CancelAsync(); + Console.SetError(consoleError); } } -#if DEBUG - [Fact] -#else - [Fact(Skip = "Skipping this test as it is not working properly.")] -#endif + [RetryFact] public async Task TestLambdaToUpperRest() { var (lambdaPort, apiGatewayPort) = await GetFreePorts(); - - var testProjectDir = Path.GetFullPath("../../../../../testapps"); - var config = new TestConfig - { - TestToolPath = Path.GetFullPath(Path.Combine(testProjectDir, "../src/Amazon.Lambda.TestTool")), - LambdaPath = Path.GetFullPath(Path.Combine(testProjectDir, "LambdaTestFunctionV1")), - FunctionName = "LambdaTestFunctionV1", - RouteName = "testfunction", - HttpMethod = "Post" - }; - - var cancellationTokenSource = new CancellationTokenSource(); - + _cancellationTokenSource = new CancellationTokenSource(); + _cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(120)); + var consoleError = Console.Error; try { - StartTestToolProcess(ApiGatewayEmulatorMode.Rest, config, lambdaPort, apiGatewayPort, cancellationTokenSource); + Console.SetError(TextWriter.Null); + await StartTestToolProcessAsync(ApiGatewayEmulatorMode.Rest, "testfunction", lambdaPort, apiGatewayPort, _cancellationTokenSource); await WaitForGatewayHealthCheck(apiGatewayPort); - await StartLambdaProcess(config, lambdaPort); - - var response = await TestEndpoint(config, apiGatewayPort); + var handler = (APIGatewayProxyRequest request, ILambdaContext context) => + { + var env = Environment.GetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API"); + testOutputHelper.WriteLine($"TestLambdaToUpperRest: {env}"); + return new APIGatewayProxyResponse() + { + StatusCode = 200, + Body = request.Body.ToUpper(), + IsBase64Encoded = false, + }; + }; + + Environment.SetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API", $"localhost:{lambdaPort}/testfunction"); + _ = LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer()) + .Build() + .RunAsync(_cancellationTokenSource.Token); + + var response = await TestEndpoint("testfunction", apiGatewayPort); var responseContent = await response.Content.ReadAsStringAsync(); Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -107,39 +106,41 @@ public async Task TestLambdaToUpperRest() } finally { - await cancellationTokenSource.CancelAsync(); + await _cancellationTokenSource.CancelAsync(); + Console.SetError(consoleError); } } -#if DEBUG - [Fact] -#else - [Fact(Skip = "Skipping this test as it is not working properly.")] -#endif + [RetryFact] public async Task TestLambdaToUpperV1() { var (lambdaPort, apiGatewayPort) = await GetFreePorts(); - - - var testProjectDir = Path.GetFullPath("../../../../../testapps"); - var config = new TestConfig - { - TestToolPath = Path.GetFullPath(Path.Combine(testProjectDir, "../src/Amazon.Lambda.TestTool")), - LambdaPath = Path.GetFullPath(Path.Combine(testProjectDir, "LambdaTestFunctionV1")), - FunctionName = "LambdaTestFunctionV1", - RouteName = "testfunction", - HttpMethod = "Post" - }; - - var cancellationTokenSource = new CancellationTokenSource(); - + _cancellationTokenSource = new CancellationTokenSource(); + _cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(120)); + var consoleError = Console.Error; try { - StartTestToolProcess(ApiGatewayEmulatorMode.HttpV1, config, lambdaPort, apiGatewayPort, cancellationTokenSource); + Console.SetError(TextWriter.Null); + await StartTestToolProcessAsync(ApiGatewayEmulatorMode.HttpV1, "testfunction", lambdaPort, apiGatewayPort, _cancellationTokenSource); await WaitForGatewayHealthCheck(apiGatewayPort); - await StartLambdaProcess(config, lambdaPort); - - var response = await TestEndpoint(config, apiGatewayPort); + var handler = (APIGatewayProxyRequest request, ILambdaContext context) => + { + var env = Environment.GetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API"); + testOutputHelper.WriteLine($"TestLambdaToUpperV1: {env}"); + return new APIGatewayProxyResponse() + { + StatusCode = 200, + Body = request.Body.ToUpper(), + IsBase64Encoded = false, + }; + }; + + Environment.SetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API", $"localhost:{lambdaPort}/testfunction"); + _ = LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer()) + .Build() + .RunAsync(_cancellationTokenSource.Token); + + var response = await TestEndpoint("testfunction", apiGatewayPort); var responseContent = await response.Content.ReadAsStringAsync(); Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -147,39 +148,52 @@ public async Task TestLambdaToUpperV1() } finally { - await cancellationTokenSource.CancelAsync(); + await _cancellationTokenSource.CancelAsync(); + Console.SetError(consoleError); } } -#if DEBUG - [Fact] -#else - [Fact(Skip = "Skipping this test as it is not working properly.")] -#endif + [RetryFact] public async Task TestLambdaBinaryResponse() { var (lambdaPort, apiGatewayPort) = await GetFreePorts(); - - - var testProjectDir = Path.GetFullPath("../../../../../testapps"); - var config = new TestConfig - { - TestToolPath = Path.GetFullPath(Path.Combine(testProjectDir, "../src/Amazon.Lambda.TestTool")), - LambdaPath = Path.GetFullPath(Path.Combine(testProjectDir, "LambdaBinaryFunction")), - FunctionName = "LambdaBinaryFunction", - RouteName = "binaryfunction", - HttpMethod = "Get" - }; - - var cancellationTokenSource = new CancellationTokenSource(); - + _cancellationTokenSource = new CancellationTokenSource(); + _cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(120)); + var consoleError = Console.Error; try { - StartTestToolProcess(ApiGatewayEmulatorMode.HttpV2, config, lambdaPort, apiGatewayPort, cancellationTokenSource); + Console.SetError(TextWriter.Null); + await StartTestToolProcessAsync(ApiGatewayEmulatorMode.HttpV2, "binaryfunction", lambdaPort, apiGatewayPort, _cancellationTokenSource); await WaitForGatewayHealthCheck(apiGatewayPort); - await StartLambdaProcess(config, lambdaPort); + var handler = (APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) => + { + var env = Environment.GetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API"); + testOutputHelper.WriteLine($"TestLambdaBinaryResponse: {env}"); + // Create a simple binary pattern (for example, counting bytes from 0 to 255) + byte[] binaryData = new byte[256]; + for (int i = 0; i < 256; i++) + { + binaryData[i] = (byte)i; + } - var response = await TestEndpoint(config, apiGatewayPort); + return new APIGatewayHttpApiV2ProxyResponse + { + StatusCode = 200, + Body = Convert.ToBase64String(binaryData), + IsBase64Encoded = true, + Headers = new Dictionary + { + { "Content-Type", "application/octet-stream" } + } + }; + }; + + Environment.SetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API", $"localhost:{lambdaPort}/binaryfunction"); + _ = LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer()) + .Build() + .RunAsync(_cancellationTokenSource.Token); + + var response = await TestEndpoint("binaryfunction", apiGatewayPort, httpMethod: "POST"); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("application/octet-stream", response.Content.Headers.ContentType?.MediaType); @@ -193,38 +207,36 @@ public async Task TestLambdaBinaryResponse() } finally { - await cancellationTokenSource.CancelAsync(); + await _cancellationTokenSource.CancelAsync(); + Console.SetError(consoleError); } } -#if DEBUG - [Fact] -#else - [Fact(Skip = "Skipping this test as it is not working properly.")] -#endif + [RetryFact] public async Task TestLambdaReturnString() { var (lambdaPort, apiGatewayPort) = await GetFreePorts(); - - var testProjectDir = Path.GetFullPath("../../../../../testapps"); - var config = new TestConfig - { - TestToolPath = Path.GetFullPath(Path.Combine(testProjectDir, "../src/Amazon.Lambda.TestTool")), - LambdaPath = Path.GetFullPath(Path.Combine(testProjectDir, "LambdaReturnStringFunction")), - FunctionName = "LambdaReturnStringFunction", - RouteName = "stringfunction", - HttpMethod = "Post" - }; - - var cancellationTokenSource = new CancellationTokenSource(); - + _cancellationTokenSource = new CancellationTokenSource(); + _cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(120)); + var consoleError = Console.Error; try { - StartTestToolProcess(ApiGatewayEmulatorMode.HttpV2, config, lambdaPort, apiGatewayPort, cancellationTokenSource); + Console.SetError(TextWriter.Null); + await StartTestToolProcessAsync(ApiGatewayEmulatorMode.HttpV2, "stringfunction", lambdaPort, apiGatewayPort, _cancellationTokenSource); await WaitForGatewayHealthCheck(apiGatewayPort); - await StartLambdaProcess(config, lambdaPort); + var handler = (APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) => + { + var env = Environment.GetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API"); + testOutputHelper.WriteLine($"TestLambdaReturnString: {env}"); + return request.Body.ToUpper(); + }; - var response = await TestEndpoint(config, apiGatewayPort); + Environment.SetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API", $"localhost:{lambdaPort}/stringfunction"); + _ = LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer()) + .Build() + .RunAsync(_cancellationTokenSource.Token); + + var response = await TestEndpoint("stringfunction", apiGatewayPort); var responseContent = await response.Content.ReadAsStringAsync(); Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -232,38 +244,40 @@ public async Task TestLambdaReturnString() } finally { - await cancellationTokenSource.CancelAsync(); + await _cancellationTokenSource.CancelAsync(); + Console.SetError(consoleError); } } -#if DEBUG - [Fact] -#else - [Fact(Skip = "Skipping this test as it is not working properly.")] -#endif + [RetryFact] public async Task TestLambdaWithNullEndpoint() { - var testProjectDir = Path.GetFullPath("../../../../../testapps"); - var config = new TestConfig - { - TestToolPath = Path.GetFullPath(Path.Combine(testProjectDir, "../src/Amazon.Lambda.TestTool")), - LambdaPath = Path.GetFullPath(Path.Combine(testProjectDir, "LambdaTestFunctionV2")), - FunctionName = "LambdaTestFunctionV2", - RouteName = "testfunction", - HttpMethod = "Post" - }; - - var cancellationTokenSource = new CancellationTokenSource(); - + var (lambdaPort, apiGatewayPort) = await GetFreePorts(); + _cancellationTokenSource = new CancellationTokenSource(); + _cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(120)); + var consoleError = Console.Error; try { - var (lambdaPort, apiGatewayPort) = await GetFreePorts(); - - StartTestToolProcessWithNullEndpoint(ApiGatewayEmulatorMode.HttpV2, lambdaPort, apiGatewayPort, config, cancellationTokenSource); + Console.SetError(TextWriter.Null); + await StartTestToolProcessWithNullEndpoint(ApiGatewayEmulatorMode.HttpV2, "testfunction", lambdaPort, apiGatewayPort, _cancellationTokenSource); await WaitForGatewayHealthCheck(apiGatewayPort); - await StartLambdaProcess(config, lambdaPort); + var handler = (APIGatewayHttpApiV2ProxyRequest request, ILambdaContext context) => + { + var env = Environment.GetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API"); + testOutputHelper.WriteLine($"TestLambdaWithNullEndpoint: {env}"); + return new APIGatewayHttpApiV2ProxyResponse + { + StatusCode = 200, + Body = request.Body.ToUpper() + }; + }; + + Environment.SetEnvironmentVariable("AWS_LAMBDA_RUNTIME_API", $"localhost:{lambdaPort}/testfunction"); + _ = LambdaBootstrapBuilder.Create(handler, new DefaultLambdaJsonSerializer()) + .Build() + .RunAsync(_cancellationTokenSource.Token); - var response = await TestEndpoint(config, apiGatewayPort); + var response = await TestEndpoint("testfunction", apiGatewayPort); var responseContent = await response.Content.ReadAsStringAsync(); Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -271,70 +285,135 @@ public async Task TestLambdaWithNullEndpoint() } finally { - await cancellationTokenSource.CancelAsync(); + await _cancellationTokenSource.CancelAsync(); + Console.SetError(consoleError); } } - private record TestConfig - { - public required string TestToolPath { get; init; } - public required string LambdaPath { get; init; } - public required string FunctionName { get; init; } - public required string RouteName { get; init; } - public required string HttpMethod { get; init; } - } - - private async Task TestEndpoint(TestConfig config, int apiGatewayPort, HttpContent? content = null) + private async Task TestEndpoint(string routeName, int apiGatewayPort, string httpMethod = "POST") { - using var client = new HttpClient(); - return config.HttpMethod.ToUpper() switch + testOutputHelper.WriteLine($"Testing endpoint: http://localhost:{apiGatewayPort}/{routeName}"); + using (var client = new HttpClient()) { - "POST" => await client.PostAsync($"http://localhost:{apiGatewayPort}/{config.RouteName}", - content ?? new StringContent("hello world", Encoding.UTF8, "text/plain")), - "GET" => await client.GetAsync($"http://localhost:{apiGatewayPort}/{config.RouteName}"), - _ => throw new ArgumentException($"Unsupported HTTP method: {config.HttpMethod}") - }; + client.Timeout = TimeSpan.FromSeconds(2); + + var startTime = DateTime.UtcNow; + var timeout = TimeSpan.FromSeconds(45); + Exception? lastException = null; + + while (DateTime.UtcNow - startTime < timeout) + { + await Task.Delay(1_000); + + try + { + return httpMethod.ToUpper() switch + { + "POST" => await client.PostAsync( + $"http://localhost:{apiGatewayPort}/{routeName}", + new StringContent("hello world", Encoding.UTF8, "text/plain")), + "GET" => await client.GetAsync($"http://localhost:{apiGatewayPort}/{routeName}"), + _ => throw new ArgumentException($"Unsupported HTTP method: {httpMethod}") + }; + } + catch (Exception ex) + { + lastException = ex; + testOutputHelper.WriteLine($"Request attempt failed - Message: {ex.Message}"); + testOutputHelper.WriteLine($"Request attempt failed - Stack Trace: {ex.StackTrace}"); + await Task.Delay(500); + } + } + + throw new TimeoutException($"Failed to complete request within timeout period: z{lastException?.Message}", lastException); + } } - private void StartTestToolProcessWithNullEndpoint(ApiGatewayEmulatorMode apiGatewayMode, int lambdaPort, int apiGatewayPort, TestConfig config, CancellationTokenSource cancellationTokenSource) + private async Task StartTestToolProcessAsync(ApiGatewayEmulatorMode apiGatewayMode, string routeName, int lambdaPort, int apiGatewayPort, CancellationTokenSource cancellationTokenSource, string httpMethod = "POST") { Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development"); Environment.SetEnvironmentVariable("APIGATEWAY_EMULATOR_ROUTE_CONFIG", $@"{{ - ""LambdaResourceName"": ""{config.RouteName}"", - ""HttpMethod"": ""{config.HttpMethod}"", - ""Path"": ""/{config.RouteName}"" + ""LambdaResourceName"": ""{routeName}"", + ""Endpoint"": ""http://localhost:{lambdaPort}"", + ""HttpMethod"": ""{httpMethod}"", + ""Path"": ""/{routeName}"" }}"); - cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(60)); - var settings = new RunCommandSettings { NoLaunchWindow = true, ApiGatewayEmulatorMode = apiGatewayMode, ApiGatewayEmulatorPort = apiGatewayPort, LambdaEmulatorPort = lambdaPort}; + var settings = new RunCommandSettings + { + LambdaEmulatorPort = lambdaPort, + NoLaunchWindow = true, + ApiGatewayEmulatorMode = apiGatewayMode, + ApiGatewayEmulatorPort = apiGatewayPort + }; var command = new RunCommand(_mockInteractiveService.Object, _mockEnvironmentManager.Object); var context = new CommandContext(new List(), _mockRemainingArgs.Object, "run", null); - _ = command.ExecuteAsync(context, settings, cancellationTokenSource); + + // Give the process time to start + await Task.Delay(2000, cancellationTokenSource.Token); } - private void StartTestToolProcess(ApiGatewayEmulatorMode apiGatewayMode, TestConfig config, int lambdaPort, int apiGatewayPort, CancellationTokenSource cancellationTokenSource) + private async Task StartTestToolProcessWithNullEndpoint(ApiGatewayEmulatorMode apiGatewayMode, string routeName, int lambdaPort, int apiGatewayPort, CancellationTokenSource cancellationTokenSource) { Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", "Development"); Environment.SetEnvironmentVariable("APIGATEWAY_EMULATOR_ROUTE_CONFIG", $@"{{ - ""LambdaResourceName"": ""{config.RouteName}"", - ""Endpoint"": ""http://localhost:{lambdaPort}"", - ""HttpMethod"": ""{config.HttpMethod}"", - ""Path"": ""/{config.RouteName}"" + ""LambdaResourceName"": ""{routeName}"", + ""HttpMethod"": ""POST"", + ""Path"": ""/{routeName}"" }}"); - cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(60)); - var settings = new RunCommandSettings { LambdaEmulatorPort = lambdaPort, NoLaunchWindow = true, ApiGatewayEmulatorMode = apiGatewayMode,ApiGatewayEmulatorPort = apiGatewayPort}; + + var settings = new RunCommandSettings + { + LambdaEmulatorPort = lambdaPort, + NoLaunchWindow = true, + ApiGatewayEmulatorMode = apiGatewayMode, + ApiGatewayEmulatorPort = apiGatewayPort + }; + var command = new RunCommand(_mockInteractiveService.Object, _mockEnvironmentManager.Object); var context = new CommandContext(new List(), _mockRemainingArgs.Object, "run", null); - - // Act _ = command.ExecuteAsync(context, settings, cancellationTokenSource); + + // Give the process time to start + await Task.Delay(2000, cancellationTokenSource.Token); + } + + private async Task WaitForGatewayHealthCheck(int apiGatewayPort) + { + using (var client = new HttpClient()) + { + client.Timeout = TimeSpan.FromSeconds(5); + var startTime = DateTime.UtcNow; + var timeout = TimeSpan.FromSeconds(30); + var healthUrl = $"http://localhost:{apiGatewayPort}/__lambda_test_tool_apigateway_health__"; + + while (DateTime.UtcNow - startTime < timeout) + { + try + { + var response = await client.GetAsync(healthUrl); + if (response.IsSuccessStatusCode) + { + testOutputHelper.WriteLine("API Gateway health check succeeded"); + // Add additional delay after successful health check + await Task.Delay(1000); + return; + } + } + catch (Exception ex) + { + testOutputHelper.WriteLine($"Health check attempt failed: {ex.Message}"); + await Task.Delay(500); + } + } + throw new TimeoutException("API Gateway failed to start within timeout period"); + } } private async Task<(int lambdaPort, int apiGatewayPort)> GetFreePorts() { - // Get two different ports var lambdaPort = GetFreePort(); int apiGatewayPort; do @@ -359,155 +438,4 @@ private int GetFreePort() listener.Stop(); } } - - private async Task StartLambdaProcess(TestConfig config, int lambdaPort) - { - // Build the project - var buildResult = await RunProcess("dotnet", "publish -c Release", config.LambdaPath); - if (buildResult.ExitCode != 0) - { - throw new Exception($"Build failed: {buildResult.Output}\n{buildResult.Error}"); - } - - var publishFolder = Path.Combine(config.LambdaPath, "bin", "Release", "net8.0"); - var archFolders = Directory.GetDirectories(publishFolder, "*"); - var archFolder = Assert.Single(archFolders); - - var startInfo = new ProcessStartInfo - { - FileName = "dotnet", - Arguments = Path.Combine(archFolder, "publish", $"{config.FunctionName}.dll"), - WorkingDirectory = config.LambdaPath, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - startInfo.EnvironmentVariables["AWS_LAMBDA_RUNTIME_API"] = $"localhost:{lambdaPort}/{config.RouteName}"; - startInfo.EnvironmentVariables["LAMBDA_TASK_ROOT"] = config.LambdaPath; - startInfo.EnvironmentVariables["AWS_LAMBDA_FUNCTION_MEMORY_SIZE"] = "256"; - startInfo.EnvironmentVariables["AWS_LAMBDA_FUNCTION_TIMEOUT"] = "30"; - startInfo.EnvironmentVariables["AWS_LAMBDA_FUNCTION_NAME"] = config.FunctionName; - startInfo.EnvironmentVariables["AWS_LAMBDA_FUNCTION_VERSION"] = "$LATEST"; - - _lambdaProcess = Process.Start(startInfo) ?? throw new Exception("Failed to start Lambda process"); - ConfigureProcessLogging(_lambdaProcess, "Lambda"); - } - - private void ConfigureProcessLogging(Process process, string prefix) - { - process.OutputDataReceived += async (_, e) => - { - if (e.Data != null) await LogMessage($"{prefix}: {e.Data}"); - }; - process.ErrorDataReceived += async (_, e) => - { - if (e.Data != null) await LogMessage($"{prefix} Error: {e.Data}"); - }; - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - } - - private async Task<(int ExitCode, string Output, string Error)> RunProcess(string fileName, string arguments, string? workingDirectory = null) - { - using var process = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = fileName, - Arguments = arguments, - WorkingDirectory = workingDirectory ?? Directory.GetCurrentDirectory(), - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - } - }; - - var output = new StringBuilder(); - var error = new StringBuilder(); - process.OutputDataReceived += (_, e) => { if (e.Data != null) output.AppendLine(e.Data); }; - process.ErrorDataReceived += (_, e) => { if (e.Data != null) error.AppendLine(e.Data); }; - - process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - await process.WaitForExitAsync(); - - return (process.ExitCode, output.ToString(), error.ToString()); - } - - private async Task WaitForGatewayHealthCheck(int apiGatewayPort) - { - using var client = new HttpClient(); - var startTime = DateTime.UtcNow; - var timeout = TimeSpan.FromSeconds(10); - var healthUrl = $"http://localhost:{apiGatewayPort}/__lambda_test_tool_apigateway_health__"; - - while (DateTime.UtcNow - startTime < timeout) - { - try - { - var response = await client.GetAsync(healthUrl); - if (response.IsSuccessStatusCode) - { - LogMessage("API Gateway health check succeeded"); - return; - } - } - catch - { - await Task.Delay(100); - } - } - throw new TimeoutException("API Gateway failed to start within timeout period"); - } - - private async Task LogMessage(string message) - { - await _logSemaphore.WaitAsync(); - try - { - Console.WriteLine(message); // Still write to console for debugging - _logMessages.Enqueue(message); - } - finally - { - _logSemaphore.Release(); - } - } - - private async Task CleanupProcesses() - { - var processes = new[] { _lambdaProcess }; - foreach (var process in processes.Where(p => p != null && !p.HasExited)) - { - try - { - process!.Kill(entireProcessTree: true); - await Task.Delay(100); - } - catch (Exception ex) - { - LogMessage($"Error killing process: {ex.Message}"); - } - finally - { - process!.Dispose(); - } - } - } - - public async ValueTask DisposeAsync() - { - // Write all queued messages before disposing - while (_logMessages.TryDequeue(out var message)) - { - _testOutputHelper.WriteLine(message); - } - - await CleanupProcesses(); - _logSemaphore.Dispose(); - } } diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.IntegrationTests/Properties/AssemblyInfo.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.IntegrationTests/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..3195e5a2a --- /dev/null +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.IntegrationTests/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +using Xunit; + +[assembly: CollectionBehavior(DisableTestParallelization = true)] diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Amazon.Lambda.TestTool.Tests.Common.csproj b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Amazon.Lambda.TestTool.Tests.Common.csproj index c270fa02b..21b4d7eb1 100644 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Amazon.Lambda.TestTool.Tests.Common.csproj +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Amazon.Lambda.TestTool.Tests.Common.csproj @@ -8,6 +8,7 @@ + diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Helpers/TestHelpers.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Helpers/TestHelpers.cs similarity index 71% rename from Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Helpers/TestHelpers.cs rename to Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Helpers/TestHelpers.cs index 1ab304195..79c618d48 100644 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Helpers/TestHelpers.cs +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Helpers/TestHelpers.cs @@ -1,13 +1,11 @@ -// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -using System.Collections.Concurrent; +namespace Amazon.Lambda.TestTool.Tests.Common.Helpers; -namespace Amazon.Lambda.TestTool.UnitTests.Helpers; - -internal static class TestHelpers +public static class TestHelpers { - internal static async Task WaitForApiToStartAsync(string url, int maxRetries = 5, int delayMilliseconds = 1000) + public static async Task WaitForApiToStartAsync(string url, int maxRetries = 5, int delayMilliseconds = 1000) { using (var client = new HttpClient()) { @@ -33,7 +31,7 @@ internal static async Task WaitForApiToStartAsync(string url, int maxRetri } } - internal static async Task SendRequest(string url) + public static async Task SendRequest(string url) { using (var client = new HttpClient()) { diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Retries/RetryFactAttribute.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Retries/RetryFactAttribute.cs new file mode 100644 index 000000000..7d5b74bd2 --- /dev/null +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Retries/RetryFactAttribute.cs @@ -0,0 +1,18 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +using Xunit; +using Xunit.Sdk; + +namespace Amazon.Lambda.TestTool.Tests.Common.Retries; + +[XunitTestCaseDiscoverer("Amazon.Lambda.TestTool.Tests.Common.Retries.RetryFactDiscoverer", "Amazon.Lambda.TestTool.Tests.Common")] +public class RetryFactAttribute : FactAttribute +{ + /// + /// Number of additional attempts (not counting the initial try) + /// + public int MaxRetries { get; set; } = 3; + + public RetryFactAttribute() { } +} diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Retries/RetryFactDiscoverer.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Retries/RetryFactDiscoverer.cs new file mode 100644 index 000000000..2723dab0e --- /dev/null +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Retries/RetryFactDiscoverer.cs @@ -0,0 +1,31 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Amazon.Lambda.TestTool.Tests.Common.Retries; + +public class RetryFactDiscoverer : IXunitTestCaseDiscoverer +{ + readonly IMessageSink diagnosticMessageSink; + + public RetryFactDiscoverer(IMessageSink diagnosticMessageSink) + { + this.diagnosticMessageSink = diagnosticMessageSink; + } + + public IEnumerable Discover(ITestFrameworkDiscoveryOptions discoveryOptions, + ITestMethod testMethod, + IAttributeInfo factAttribute) + { + // Read the MaxRetries property from the attribute. + int maxRetries = factAttribute.GetNamedArgument("MaxRetries"); + + yield return new RetryTestCase(diagnosticMessageSink, + discoveryOptions.MethodDisplayOrDefault(), + discoveryOptions.MethodDisplayOptionsOrDefault(), + testMethod, + maxRetries); + } +} diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Retries/RetryTestCase.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Retries/RetryTestCase.cs new file mode 100644 index 000000000..0794d19f1 --- /dev/null +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.Tests.Common/Retries/RetryTestCase.cs @@ -0,0 +1,76 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Amazon.Lambda.TestTool.Tests.Common.Retries; + +public class RetryTestCase : XunitTestCase +{ + int _maxRetries; + + public RetryTestCase(IMessageSink diagnosticMessageSink, + TestMethodDisplay defaultMethodDisplay, + TestMethodDisplayOptions defaultMethodDisplayOptions, + ITestMethod testMethod, + int maxRetries) + : base(diagnosticMessageSink, defaultMethodDisplay, defaultMethodDisplayOptions, testMethod) + { + _maxRetries = maxRetries; + } + + // Parameterless constructor needed for de-serialization + [Obsolete("Called by the de-serializer", true)] + public RetryTestCase() { } + + public override async Task RunAsync(IMessageSink diagnosticMessageSink, + IMessageBus messageBus, + object[] constructorArguments, + ExceptionAggregator aggregator, + CancellationTokenSource cancellationTokenSource) + { + RunSummary? finalSummary = null; + + for (int attempt = 0; attempt <= _maxRetries; attempt++) + { + // Create a fresh aggregator for each attempt + var attemptAggregator = new ExceptionAggregator(); + + // Run the test (each attempt returns its own summary) + var currentSummary = await base.RunAsync(diagnosticMessageSink, + messageBus, + constructorArguments, + attemptAggregator, + cancellationTokenSource); + + if (currentSummary.Failed == 0) + { + // If the test passed, log a message and return the current summary + if (attempt > 0) + { + diagnosticMessageSink.OnMessage(new DiagnosticMessage($"Test passed on attempt {attempt + 1}")); + } + return currentSummary; + } + + diagnosticMessageSink.OnMessage(new DiagnosticMessage($"Test failed on attempt {attempt + 1}, retrying...")); + finalSummary = currentSummary; + } + + // If none of the attempts passed, return the summary of the final attempt. + return finalSummary; + } + + public override void Serialize(IXunitSerializationInfo data) + { + base.Serialize(data); + data.AddValue("MaxRetries", _maxRetries); + } + + public override void Deserialize(IXunitSerializationInfo data) + { + base.Deserialize(data); + _maxRetries = data.GetValue("MaxRetries"); + } +} diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Commands/RunCommandTests.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Commands/RunCommandTests.cs index f716a6473..5dcdf7934 100644 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Commands/RunCommandTests.cs +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Commands/RunCommandTests.cs @@ -7,9 +7,9 @@ using Amazon.Lambda.TestTool.Services; using Spectre.Console.Cli; using Moq; -using Amazon.Lambda.TestTool.UnitTests.Helpers; using Xunit; using Amazon.Lambda.TestTool.Services.IO; +using Amazon.Lambda.TestTool.Tests.Common.Helpers; using Amazon.Lambda.TestTool.Utilities; using System.Text.Json.Nodes; @@ -21,11 +21,7 @@ public class RunCommandTests private readonly Mock _mockInteractiveService = new Mock(); private readonly Mock _mockRemainingArgs = new Mock(); -#if DEBUG [Fact] -#else - [Fact(Skip = "Skipping this test as it is not working properly.")] -#endif public async Task ExecuteAsync_LambdaRuntimeApi_SuccessfulLaunch() { // Arrange @@ -49,11 +45,7 @@ public async Task ExecuteAsync_LambdaRuntimeApi_SuccessfulLaunch() Assert.True(isApiRunning); } -#if DEBUG [Fact] -#else - [Fact(Skip = "Skipping this test as it is not working properly.")] -#endif public async Task ExecuteAsync_ApiGatewayEmulator_SuccessfulLaunch() { // Arrange @@ -78,11 +70,7 @@ public async Task ExecuteAsync_ApiGatewayEmulator_SuccessfulLaunch() Assert.True(isApiRunning); } -#if DEBUG [Fact] -#else - [Fact(Skip = "Skipping this test as it is not working properly.")] -#endif public async Task ExecuteAsync_EnvPorts_SuccessfulLaunch() { var lambdaPort = TestHelpers.GetNextLambdaRuntimePort(); diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/PackagingTests.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/PackagingTests.cs index e5c68bd3b..7d47bf3ab 100644 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/PackagingTests.cs +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/PackagingTests.cs @@ -9,7 +9,6 @@ namespace Amazon.Lambda.TestTool.UnitTests; public class PackagingTests : IDisposable { private readonly ITestOutputHelper _output; - private readonly string[] _expectedFrameworks; private readonly string _workingDirectory; public PackagingTests(ITestOutputHelper output) @@ -17,61 +16,20 @@ public PackagingTests(ITestOutputHelper output) _output = output; var solutionRoot = FindSolutionRoot(); _workingDirectory = DirectoryHelpers.GetTempTestAppDirectory(solutionRoot); - _expectedFrameworks = GetRuntimeSupportTargetFrameworks() - .Split([';'], StringSplitOptions.RemoveEmptyEntries) - .Where(f => f != "netstandard2.0") - .ToArray(); } - private string GetRuntimeSupportTargetFrameworks() - { - 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 - { - StartInfo = new ProcessStartInfo - { - FileName = "dotnet", - Arguments = $"msbuild {runtimeSupportPath} --getProperty:TargetFrameworks", - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false, - CreateNoWindow = true, - } - }; - - process.Start(); - var output = process.StandardOutput.ReadToEnd(); - var error = process.StandardError.ReadToEnd(); - process.WaitForExit(int.MaxValue); - - Console.WriteLine(output); - Console.WriteLine(error); - if (process.ExitCode != 0) - { - throw new Exception($"Failed to get TargetFrameworks: {error}"); - } - - return output.Trim(); - } - -#if DEBUG [Fact] -#else - [Fact(Skip = "Skipping this test as it is not working properly.")] -#endif public void VerifyPackageContentsHasRuntimeSupport() { var projectPath = Path.Combine(_workingDirectory, "Tools", "LambdaTestTool-v2", "src", "Amazon.Lambda.TestTool", "Amazon.Lambda.TestTool.csproj"); - - _output.WriteLine("\nPacking TestTool..."); + var expectedFrameworks = new string[] { "net6.0", "net8.0", "net9.0" }; + _output.WriteLine("Packing TestTool..."); var packProcess = new Process { StartInfo = new ProcessStartInfo { FileName = "dotnet", - Arguments = $"pack {projectPath} -c Release", + Arguments = $"pack -c Release --no-build --no-restore {projectPath}", RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, @@ -105,7 +63,7 @@ public void VerifyPackageContentsHasRuntimeSupport() using var archive = ZipFile.OpenRead(packagePath); // Verify each framework has its required files - foreach (var framework in _expectedFrameworks) + foreach (var framework in expectedFrameworks) { _output.WriteLine($"\nChecking framework: {framework}"); @@ -146,14 +104,17 @@ private string FindSolutionRoot() string? currentDirectory = Directory.GetCurrentDirectory(); while (currentDirectory != null) { - // Look for the aws-lambda-dotnet directory specifically - if (Path.GetFileName(currentDirectory) == "aws-lambda-dotnet") + // Look for the "Tools" directory specifically and then go up one level to the root of the repository. + // The reason we do this is because the source directory "aws-lambda-dotnet" does not always exist in the CI. + // In CodeBuild, the contents of "aws-lambda-dotnet" get copied to a temp location, + // so the path does not contain the name "aws-lambda-dotnet". + if (Path.GetFileName(currentDirectory) == "Tools") { - return currentDirectory; + return Path.Combine(currentDirectory, ".."); } currentDirectory = Directory.GetParent(currentDirectory)?.FullName; } - throw new Exception("Could not find the aws-lambda-dotnet root directory."); + throw new Exception("Could not find the 'Tools' root directory."); } public void Dispose() diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Processes/ApiGatewayEmulatorProcessTests.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Processes/ApiGatewayEmulatorProcessTests.cs index e454cceef..f6d80d58b 100644 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Processes/ApiGatewayEmulatorProcessTests.cs +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Processes/ApiGatewayEmulatorProcessTests.cs @@ -5,7 +5,7 @@ using Amazon.Lambda.TestTool.Commands.Settings; using Amazon.Lambda.TestTool.Models; using Amazon.Lambda.TestTool.Processes; -using Amazon.Lambda.TestTool.UnitTests.Helpers; +using Amazon.Lambda.TestTool.Tests.Common.Helpers; using Xunit; namespace Amazon.Lambda.TestTool.UnitTests.Processes; diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Properties/AssemblyInfo.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Properties/AssemblyInfo.cs index e69de29bb..3195e5a2a 100644 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Properties/AssemblyInfo.cs +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +using Xunit; + +[assembly: CollectionBehavior(DisableTestParallelization = true)] diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/RuntimeApiTests.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/RuntimeApiTests.cs index 25e0c656f..4194bde1f 100644 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/RuntimeApiTests.cs +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/RuntimeApiTests.cs @@ -9,7 +9,8 @@ using Amazon.Lambda.Core; using Amazon.Lambda.TestTool.Processes; using Amazon.Lambda.TestTool.Commands.Settings; -using Amazon.Lambda.TestTool.UnitTests.Helpers; +using Amazon.Lambda.TestTool.Tests.Common.Helpers; +using Amazon.Lambda.TestTool.Tests.Common.Retries; using Microsoft.Extensions.DependencyInjection; using Xunit; using Environment = System.Environment; @@ -75,11 +76,7 @@ public async Task AddEventToDataStore() } } -#if DEBUG - [Fact] -#else - [Fact(Skip = "Skipping this test as it is not working properly.")] -#endif + [RetryFact] public async Task InvokeRequestResponse() { const string functionName = "FunctionFoo"; @@ -133,7 +130,6 @@ public async Task InvokeRequestResponse() { await cancellationTokenSource.CancelAsync(); } - } private IAmazonLambda ConstructLambdaServiceClient(string url) diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Services/LambdaRuntimeApiTests.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Services/LambdaRuntimeApiTests.cs index 26ebe7de4..3b6b8e297 100644 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Services/LambdaRuntimeApiTests.cs +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Services/LambdaRuntimeApiTests.cs @@ -151,8 +151,6 @@ public async Task PostEvent_RequestResponse_ErrorWithoutBody() Assert.Empty(responseBody); } - - [Fact] public async Task GetNextInvocation_Returns_Event() { @@ -190,75 +188,99 @@ public async Task GetNextInvocation_Returns_Event() [Fact] public void PostInitError_Logs_Error() { - // Arrange - var functionName = "testFunction"; - var errorType = "InitializationError"; - var error = "Failed to initialize"; - - // Act - var result = new LambdaRuntimeApi(_app).PostInitError(functionName, errorType, error); - - // Assert - Assert.NotNull(result); - var statusResponse = Assert.IsType((result as IValueHttpResult)?.Value); - Assert.Equal("success", statusResponse.Status); + var consoleError = Console.Error; + try + { + Console.SetError(TextWriter.Null); + // Arrange + var functionName = "testFunction"; + var errorType = "InitializationError"; + var error = "Failed to initialize"; + + // Act + var result = new LambdaRuntimeApi(_app).PostInitError(functionName, errorType, error); + + // Assert + Assert.NotNull(result); + var statusResponse = Assert.IsType((result as IValueHttpResult)?.Value); + Assert.Equal("success", statusResponse.Status); + } + finally + { + Console.SetError(consoleError); + } } [Fact] public async Task PostInvocationResponse_Reports_Success() { - // Arrange - var functionName = "testFunction"; - var awsRequestId = "request123"; - var response = "{\"result\":\"success\"}"; + var consoleError = Console.Error; + try + { + Console.SetError(TextWriter.Null); + // Arrange + var functionName = "testFunction"; + var awsRequestId = "request123"; + var response = "{\"result\":\"success\"}"; - var context = new DefaultHttpContext(); - context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(response)); - context.Response.Body = new MemoryStream(); + var context = new DefaultHttpContext(); + context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(response)); + context.Response.Body = new MemoryStream(); - _mockRuntimeDataStore - .Setup(x => x.ReportSuccess(awsRequestId, response)); + _mockRuntimeDataStore + .Setup(x => x.ReportSuccess(awsRequestId, response)); - // Act - var result = await new LambdaRuntimeApi(_app).PostInvocationResponse(context, functionName, awsRequestId); + // Act + var result = await new LambdaRuntimeApi(_app).PostInvocationResponse(context, functionName, awsRequestId); - // Assert - Assert.NotNull(result); - var statusResponse = Assert.IsType((result as IValueHttpResult)?.Value); - Assert.Equal("success", statusResponse.Status); + // Assert + Assert.NotNull(result); + var statusResponse = Assert.IsType((result as IValueHttpResult)?.Value); + Assert.Equal("success", statusResponse.Status); - _mockRuntimeDataStore.Verify(x => x.ReportSuccess(awsRequestId, response), Times.Once); + _mockRuntimeDataStore.Verify(x => x.ReportSuccess(awsRequestId, response), Times.Once); + } + finally + { + Console.SetError(consoleError); + } } [Fact] public async Task PostError_Reports_Error() { - // Arrange - var functionName = "testFunction"; - var awsRequestId = "request123"; - var errorType = "HandlerError"; - var errorBody = "Function execution failed"; + var consoleError = Console.Error; + try + { + Console.SetError(TextWriter.Null); + // Arrange + var functionName = "testFunction"; + var awsRequestId = "request123"; + var errorType = "HandlerError"; + var errorBody = "Function execution failed"; - var context = new DefaultHttpContext(); - context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(errorBody)); - context.Response.Body = new MemoryStream(); + var context = new DefaultHttpContext(); + context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(errorBody)); + context.Response.Body = new MemoryStream(); - _mockRuntimeDataStore - .Setup(x => x.ReportError(awsRequestId, errorType, errorBody)); + _mockRuntimeDataStore + .Setup(x => x.ReportError(awsRequestId, errorType, errorBody)); - // Act - var result = await new LambdaRuntimeApi(_app).PostError(context, functionName, awsRequestId, errorType); + // Act + var result = await new LambdaRuntimeApi(_app).PostError(context, functionName, awsRequestId, errorType); - // Assert - Assert.NotNull(result); - var statusResponse = Assert.IsType((result as IValueHttpResult)?.Value); - Assert.Equal("success", statusResponse.Status); + // Assert + Assert.NotNull(result); + var statusResponse = Assert.IsType((result as IValueHttpResult)?.Value); + Assert.Equal("success", statusResponse.Status); - _mockRuntimeDataStore.Verify(x => x.ReportError(awsRequestId, errorType, errorBody), Times.Once); + _mockRuntimeDataStore.Verify(x => x.ReportError(awsRequestId, errorType, errorBody), Times.Once); + } + finally + { + Console.SetError(consoleError); + } } - - - } // Helper class to prevent stream from being closed diff --git a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Utilities/DirectoryHelpers.cs b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Utilities/DirectoryHelpers.cs index 19d52cd10..6599cf13d 100644 --- a/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Utilities/DirectoryHelpers.cs +++ b/Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.UnitTests/Utilities/DirectoryHelpers.cs @@ -63,7 +63,7 @@ private static void CopyDirectory(DirectoryInfo dir, string destDirName) File.SetAttributes(tempPath, FileAttributes.Normal); } - foreach (var subdir in dirs) + foreach (var subdir in dirs.Where(x => !x.Name.Equals(".git"))) { var tempPath = Path.Combine(destDirName, subdir.Name); var subDir = new DirectoryInfo(subdir.FullName); diff --git a/buildtools/build.proj b/buildtools/build.proj index bb9ac9a43..24afe9a00 100644 --- a/buildtools/build.proj +++ b/buildtools/build.proj @@ -193,6 +193,23 @@ + + + + + + + + + + diff --git a/buildtools/ci.buildspec.yml b/buildtools/ci.buildspec.yml index 15f532844..320930898 100644 --- a/buildtools/ci.buildspec.yml +++ b/buildtools/ci.buildspec.yml @@ -12,7 +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 test -c Release --logger "console;verbosity=detailed" ./Tools/LambdaTestTool-v2/tests/Amazon.Lambda.TestTool.IntegrationTests/Amazon.Lambda.TestTool.IntegrationTests.csproj + - dotnet msbuild buildtools/build.proj /t:testtoolv2-tests /p:Cicd=true - dotnet msbuild buildtools/build.proj /t:unit-tests /p:Cicd=true - dotnet msbuild buildtools/build.proj /t:integ-tests /p:Cicd=true \ No newline at end of file