diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index d42e4ae2..b537fad0 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -1,6 +1,6 @@ # PROCESS # -# 1. Deploy the core and AOT stacks using the infra deployment workflow. +# 1. Deploy the E2E stacks using the infra deployment workflow for non-aot and aot. # 2. Run the E2E tests after the infrastructure is deployed. # 3. Destroy the CDK stacks after the tests are completed. @@ -50,10 +50,10 @@ jobs: - name: Install AWS Lambda .NET CLI Tools run: dotnet tool install -g Amazon.Lambda.Tools - - name: Deploy Core Stack + - name: Deploy Stack run: | cd libraries/tests/e2e/infra - cdk deploy --require-approval never + cdk deploy --all --require-approval never deploy-aot-stack: strategy: @@ -90,11 +90,15 @@ jobs: - name: Deploy AOT Stack run: | cd libraries/tests/e2e/infra-aot - cdk deploy -c architecture=${{ matrix.arch }} --require-approval never + cdk deploy --all -c architecture=${{ matrix.arch }} --require-approval never run-tests: + strategy: + matrix: + utility: [core, idempotency] runs-on: ubuntu-latest needs: [deploy-stack,deploy-aot-stack] + steps: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 @@ -111,14 +115,14 @@ jobs: with: dotnet-version: '8.x' - - name: Run Core Tests + - name: Run Tests run: | - cd libraries/tests/e2e/functions/core + cd libraries/tests/e2e/functions/${{ matrix.utility }} dotnet test --filter Category!=AOT - - name: Run Core AOT Tests + - name: Run AOT Tests run: | - cd libraries/tests/e2e/functions/core + cd libraries/tests/e2e/functions/${{ matrix.utility }} dotnet test --filter Category=AOT destroy-stack: @@ -142,10 +146,10 @@ jobs: - name: Install AWS Lambda .NET CLI Tools run: dotnet tool install -g Amazon.Lambda.Tools - - name: Destroy Core Stack + - name: Destroy Stack run: | cd libraries/tests/e2e/infra - cdk destroy --force + cdk destroy --all --force destroy-aot-stack: strategy: @@ -176,8 +180,8 @@ jobs: - name: Install AWS Lambda .NET CLI Tools run: dotnet tool install -g Amazon.Lambda.Tools - - name: Destroy arm64 AOT Core Stack + - name: Destroy arm64 AOT Stack run: | cd libraries/tests/e2e/infra-aot - cdk destroy -c architecture=${{ matrix.arch }} --force + cdk destroy --all -c architecture=${{ matrix.arch }} --force diff --git a/libraries/AWS.Lambda.Powertools.sln b/libraries/AWS.Lambda.Powertools.sln index 05638eb5..ba9f2700 100644 --- a/libraries/AWS.Lambda.Powertools.sln +++ b/libraries/AWS.Lambda.Powertools.sln @@ -47,13 +47,13 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Infra", "Infra", "{93DEAC72-245F-4FC9-A7B5-DAE7EF7E1AB7}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Functions", "Functions", "{CDAE55EB-9438-4F54-B7ED-931D64324D5F}" + ProjectSection(SolutionItems) = preProject + tests\e2e\functions\payload.json = tests\e2e\functions\payload.json + EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Infra", "tests\e2e\infra\Infra.csproj", "{AA532674-A61C-41E6-8F9A-ED53D79AF1EC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{AAFA39E9-66A3-4B9A-AFE9-EAF74A85A7F0}" - ProjectSection(SolutionItems) = preProject - tests\e2e\functions\core\payload.json = tests\e2e\functions\core\payload.json - EndProjectSection EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtils", "tests\e2e\functions\TestUtils\TestUtils.csproj", "{3C6162D7-0162-4BC2-BBF5-0554539A81CD}" EndProject @@ -83,6 +83,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-Function", "tests\e2e\f EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfraAot", "tests\e2e\infra-aot\InfraAot.csproj", "{24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfraShared", "tests\e2e\InfraShared\InfraShared.csproj", "{D303B458-9D84-4DDF-8781-2C0211672329}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Idempotency", "Idempotency", "{FB2C7DA3-6FCE-429D-86F9-5775D0231EC6}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function", "tests\e2e\functions\idempotency\Function\src\Function\Function.csproj", "{9AF99F6D-E8E7-443F-A965-D55B8E388836}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function.Tests", "tests\e2e\functions\idempotency\Function\test\Function.Tests\Function.Tests.csproj", "{FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AOT-Function", "tests\e2e\functions\idempotency\AOT-Function\src\AOT-Function\AOT-Function.csproj", "{56DFC68A-3994-43CD-A17C-323495F1709C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -432,6 +442,54 @@ Global {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|x64.Build.0 = Release|Any CPU {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|x86.ActiveCfg = Release|Any CPU {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95}.Release|x86.Build.0 = Release|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|x64.ActiveCfg = Debug|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|x64.Build.0 = Debug|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|x86.ActiveCfg = Debug|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Debug|x86.Build.0 = Debug|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Release|Any CPU.Build.0 = Release|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Release|x64.ActiveCfg = Release|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Release|x64.Build.0 = Release|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Release|x86.ActiveCfg = Release|Any CPU + {D303B458-9D84-4DDF-8781-2C0211672329}.Release|x86.Build.0 = Release|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|x64.ActiveCfg = Debug|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|x64.Build.0 = Debug|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|x86.ActiveCfg = Debug|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Debug|x86.Build.0 = Debug|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|Any CPU.Build.0 = Release|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|x64.ActiveCfg = Release|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|x64.Build.0 = Release|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|x86.ActiveCfg = Release|Any CPU + {9AF99F6D-E8E7-443F-A965-D55B8E388836}.Release|x86.Build.0 = Release|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|x64.ActiveCfg = Debug|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|x64.Build.0 = Debug|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|x86.ActiveCfg = Debug|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Debug|x86.Build.0 = Debug|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|Any CPU.Build.0 = Release|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|x64.ActiveCfg = Release|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|x64.Build.0 = Release|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|x86.ActiveCfg = Release|Any CPU + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC}.Release|x86.Build.0 = Release|Any CPU + {56DFC68A-3994-43CD-A17C-323495F1709C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {56DFC68A-3994-43CD-A17C-323495F1709C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {56DFC68A-3994-43CD-A17C-323495F1709C}.Debug|x64.ActiveCfg = Debug|Any CPU + {56DFC68A-3994-43CD-A17C-323495F1709C}.Debug|x64.Build.0 = Debug|Any CPU + {56DFC68A-3994-43CD-A17C-323495F1709C}.Debug|x86.ActiveCfg = Debug|Any CPU + {56DFC68A-3994-43CD-A17C-323495F1709C}.Debug|x86.Build.0 = Debug|Any CPU + {56DFC68A-3994-43CD-A17C-323495F1709C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {56DFC68A-3994-43CD-A17C-323495F1709C}.Release|Any CPU.Build.0 = Release|Any CPU + {56DFC68A-3994-43CD-A17C-323495F1709C}.Release|x64.ActiveCfg = Release|Any CPU + {56DFC68A-3994-43CD-A17C-323495F1709C}.Release|x64.Build.0 = Release|Any CPU + {56DFC68A-3994-43CD-A17C-323495F1709C}.Release|x86.ActiveCfg = Release|Any CPU + {56DFC68A-3994-43CD-A17C-323495F1709C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution @@ -470,5 +528,10 @@ Global {8DDAFE37-ED59-4710-9415-8EBA44CC6437} = {3C9FA701-31FF-4747-B324-E0D252EAFD63} {8DDED681-AE8D-45EB-A22E-2FFB88620F9B} = {3C9FA701-31FF-4747-B324-E0D252EAFD63} {24AC34AD-AEC9-4CFB-BB01-C3C81938AB95} = {93DEAC72-245F-4FC9-A7B5-DAE7EF7E1AB7} + {D303B458-9D84-4DDF-8781-2C0211672329} = {93DEAC72-245F-4FC9-A7B5-DAE7EF7E1AB7} + {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} = {CDAE55EB-9438-4F54-B7ED-931D64324D5F} + {9AF99F6D-E8E7-443F-A965-D55B8E388836} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} + {FBCE2C8A-2F64-4B62-8CF1-D4A14C19A5CC} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} + {56DFC68A-3994-43CD-A17C-323495F1709C} = {FB2C7DA3-6FCE-429D-86F9-5775D0231EC6} EndGlobalSection EndGlobal diff --git a/libraries/tests/e2e/infra/FunctionConstruct.cs b/libraries/tests/e2e/InfraShared/FunctionConstruct.cs similarity index 60% rename from libraries/tests/e2e/infra/FunctionConstruct.cs rename to libraries/tests/e2e/InfraShared/FunctionConstruct.cs index 127d1875..63fabbd3 100644 --- a/libraries/tests/e2e/infra/FunctionConstruct.cs +++ b/libraries/tests/e2e/InfraShared/FunctionConstruct.cs @@ -1,18 +1,24 @@ -using System.Collections.Generic; using Amazon.CDK; using Amazon.CDK.AWS.Lambda; using Constructs; -using TestUtils; -namespace Infra; +namespace InfraShared; public class FunctionConstruct : Construct { + public Function Function { get; set; } + public FunctionConstruct(Construct scope, string id, FunctionConstructProps props) : base(scope, id) { var framework = props.Runtime == Runtime.DOTNET_6 ? "net6.0" : "net8.0"; var distPath = $"{props.DistPath}/deploy_{props.Architecture.Name}_{props.Runtime.Name}.zip"; - _ = new Function(this, id, new FunctionProps + var command = props.IsAot + ? $"dotnet-lambda package -pl {props.SourcePath} -cmd ../../../ -o {distPath} -f net8.0 -farch {props.Architecture.Name} -cifb public.ecr.aws/sam/build-dotnet8" + : $"dotnet-lambda package -pl {props.SourcePath} -o {distPath} -f {framework} -farch {props.Architecture.Name}"; + + Console.WriteLine(command); + + Function = new Function(this, id, new FunctionProps { Runtime = props.Runtime, Architecture = props.Architecture, @@ -20,13 +26,14 @@ public FunctionConstruct(Construct scope, string id, FunctionConstructProps prop Handler = props.Handler, Tracing = Tracing.ACTIVE, Timeout = Duration.Seconds(10), + Environment = props.Environment, Code = Code.FromCustomCommand(distPath, [ - $"dotnet-lambda package -pl {props.SourcePath} -o {distPath} -f {framework} -farch {props.Architecture.Name}" + command ], new CustomCommandOptions { - CommandOptions = new Dictionary {{"shell", true }} + CommandOptions = new Dictionary { { "shell", true } } }) }); } diff --git a/libraries/tests/e2e/InfraShared/FunctionConstructProps.cs b/libraries/tests/e2e/InfraShared/FunctionConstructProps.cs new file mode 100644 index 00000000..2ae564c5 --- /dev/null +++ b/libraries/tests/e2e/InfraShared/FunctionConstructProps.cs @@ -0,0 +1,26 @@ +using Amazon.CDK; +using Amazon.CDK.AWS.Lambda; + +namespace InfraShared; + +public class FunctionConstructProps : PowertoolsDefaultStackProps +{ + public required Architecture Architecture { get; set; } + public required Runtime Runtime { get; set; } + public required string Name { get; set; } + public required string Handler { get; set; } + public required string SourcePath { get; set; } + public required string DistPath { get; set; } +} + +public class PowertoolsDefaultStackProps : StackProps +{ + public bool IsAot { get; set; } = false; + public string? ArchitectureString { get; set; } + public Dictionary? Environment { get; set; } +} + +public class IdempotencyStackProps : PowertoolsDefaultStackProps +{ + public required string TableName { get; set; } +} \ No newline at end of file diff --git a/libraries/tests/e2e/InfraShared/IdempotencyStack.cs b/libraries/tests/e2e/InfraShared/IdempotencyStack.cs new file mode 100644 index 00000000..5c66b252 --- /dev/null +++ b/libraries/tests/e2e/InfraShared/IdempotencyStack.cs @@ -0,0 +1,81 @@ +using Amazon.CDK; +using Amazon.CDK.AWS.DynamoDB; +using Amazon.CDK.AWS.Lambda; +using Constructs; +using Attribute = Amazon.CDK.AWS.DynamoDB.Attribute; + +namespace InfraShared; + +public class IdempotencyStack : Stack +{ + public Table Table { get; set; } + + public IdempotencyStack(Construct scope, string id, IdempotencyStackProps props) : base(scope, id, props) + { + Table = new Table(this, "Idempotency", new TableProps + { + PartitionKey = new Attribute + { + Name = "id", + Type = AttributeType.STRING + }, + TableName = props.TableName, + BillingMode = BillingMode.PAY_PER_REQUEST, + TimeToLiveAttribute = "expiration", + RemovalPolicy = RemovalPolicy.DESTROY + }); + + var utility = "idempotency"; + + if (props.IsAot) + { + var baseAotPath = $"../functions/{utility}/AOT-Function/src/AOT-Function"; + var distAotPath = $"../functions/{utility}/AOT-Function/dist"; + var path = new Path(baseAotPath, distAotPath); + + var architecture = props.ArchitectureString == "arm64" ? Architecture.ARM_64 : Architecture.X86_64; + var arch = architecture == Architecture.X86_64 ? "X64" : "ARM"; + CreateFunctionConstruct(this, $"{utility}_{arch}_aot_net8", Runtime.DOTNET_8, architecture, + $"E2ETestLambda_{arch}_AOT_NET8_{utility}", path, props); + } + else + { + var basePath = $"../functions/{utility}/Function/src/Function"; + var distPath = $"../functions/{utility}/Function/dist"; + var path = new Path(basePath, distPath); + + CreateFunctionConstruct(this, $"{utility}_X64_net8", Runtime.DOTNET_8, Architecture.X86_64, + $"E2ETestLambda_X64_NET8_{utility}", path, props); + CreateFunctionConstruct(this, $"{utility}_arm_net8", Runtime.DOTNET_8, Architecture.ARM_64, + $"E2ETestLambda_ARM_NET8_{utility}", path, props); + CreateFunctionConstruct(this, $"{utility}_X64_net6", Runtime.DOTNET_6, Architecture.X86_64, + $"E2ETestLambda_X64_NET6_{utility}", path, props); + CreateFunctionConstruct(this, $"{utility}_arm_net6", Runtime.DOTNET_6, Architecture.ARM_64, + $"E2ETestLambda_ARM_NET6_{utility}", path, props); + } + } + + private void CreateFunctionConstruct(Construct scope, string id, Runtime runtime, Architecture architecture, + string name,Path path, PowertoolsDefaultStackProps props) + { + var lambdaFunction = new FunctionConstruct(scope, id, new FunctionConstructProps + { + Runtime = runtime, + Architecture = architecture, + Name = name, + Handler = props.IsAot ? "AOT-Function" : "Function::Function.Function::FunctionHandler", + SourcePath = path.SourcePath, + DistPath = path.DistPath, + Environment = new Dictionary + { + { "IDEMPOTENCY_TABLE_NAME", Table.TableName } + }, + IsAot = props.IsAot + }); + + // Grant the Lambda function permissions to perform all actions on the DynamoDB table + Table.GrantReadWriteData(lambdaFunction.Function); + } +} + +public record Path(string SourcePath, string DistPath); diff --git a/libraries/tests/e2e/InfraShared/InfraShared.csproj b/libraries/tests/e2e/InfraShared/InfraShared.csproj new file mode 100644 index 00000000..dc08a5e1 --- /dev/null +++ b/libraries/tests/e2e/InfraShared/InfraShared.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + InfraShared + enable + enable + + + + + + + + + + diff --git a/libraries/tests/e2e/functions/TestUtils/FunctionConstructProps.cs b/libraries/tests/e2e/functions/TestUtils/FunctionConstructProps.cs deleted file mode 100644 index 85aa0666..00000000 --- a/libraries/tests/e2e/functions/TestUtils/FunctionConstructProps.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Amazon.CDK.AWS.Lambda; - -namespace TestUtils; - -public class FunctionConstructProps -{ - public required Architecture Architecture { get; set; } - public required Runtime Runtime { get; set; } - public required string Name { get; set; } - public required string Handler { get; set; } - public required string SourcePath { get; set; } - public required string DistPath { get; set; } -} \ No newline at end of file diff --git a/libraries/tests/e2e/functions/core/logging/Function/test/Function.Tests/FunctionTests.cs b/libraries/tests/e2e/functions/core/logging/Function/test/Function.Tests/FunctionTests.cs index 587cc27f..ca3a857a 100644 --- a/libraries/tests/e2e/functions/core/logging/Function/test/Function.Tests/FunctionTests.cs +++ b/libraries/tests/e2e/functions/core/logging/Function/test/Function.Tests/FunctionTests.cs @@ -45,7 +45,7 @@ internal async Task TestFunction(string functionName) { FunctionName = functionName, InvocationType = InvocationType.RequestResponse, - Payload = await File.ReadAllTextAsync("../../../../../../../payload.json"), + Payload = await File.ReadAllTextAsync("../../../../../../../../payload.json"), LogType = LogType.Tail }; diff --git a/libraries/tests/e2e/functions/core/metrics/Function/test/Function.Tests/FunctionTests.cs b/libraries/tests/e2e/functions/core/metrics/Function/test/Function.Tests/FunctionTests.cs index 647d3669..1670dceb 100644 --- a/libraries/tests/e2e/functions/core/metrics/Function/test/Function.Tests/FunctionTests.cs +++ b/libraries/tests/e2e/functions/core/metrics/Function/test/Function.Tests/FunctionTests.cs @@ -23,7 +23,7 @@ public FunctionTests(ITestOutputHelper testOutputHelper) [Trait("Category", "AOT")] [Theory] [InlineData("E2ETestLambda_X64_AOT_NET8_metrics")] - // [InlineData("E2ETestLambda_ARM_AOT_NET8_metrics")] + [InlineData("E2ETestLambda_ARM_AOT_NET8_metrics")] public async Task AotFunctionTest(string functionName) { await TestFunction(functionName); @@ -45,7 +45,7 @@ internal async Task TestFunction(string functionName) { FunctionName = functionName, InvocationType = InvocationType.RequestResponse, - Payload = await File.ReadAllTextAsync("../../../../../../../payload.json"), + Payload = await File.ReadAllTextAsync("../../../../../../../../payload.json"), LogType = LogType.Tail }; diff --git a/libraries/tests/e2e/functions/core/tracing/Function/test/Function.Tests/FunctionTests.cs b/libraries/tests/e2e/functions/core/tracing/Function/test/Function.Tests/FunctionTests.cs index 9203a1d5..aa1c0b39 100644 --- a/libraries/tests/e2e/functions/core/tracing/Function/test/Function.Tests/FunctionTests.cs +++ b/libraries/tests/e2e/functions/core/tracing/Function/test/Function.Tests/FunctionTests.cs @@ -26,7 +26,7 @@ public FunctionTests(ITestOutputHelper testOutputHelper) [Trait("Category", "AOT")] [Theory] [InlineData("E2ETestLambda_X64_AOT_NET8_tracing")] - // [InlineData("E2ETestLambda_ARM_AOT_NET8_tracing")] + [InlineData("E2ETestLambda_ARM_AOT_NET8_tracing")] public async Task AotFunctionTest(string functionName) { await TestFunction(functionName); @@ -48,7 +48,7 @@ internal async Task TestFunction(string functionName) { FunctionName = functionName, InvocationType = InvocationType.RequestResponse, - Payload = await File.ReadAllTextAsync("../../../../../../../payload.json"), + Payload = await File.ReadAllTextAsync("../../../../../../../../payload.json"), LogType = LogType.Tail }; diff --git a/libraries/tests/e2e/functions/idempotency/AOT-Function/src/AOT-Function/AOT-Function.csproj b/libraries/tests/e2e/functions/idempotency/AOT-Function/src/AOT-Function/AOT-Function.csproj new file mode 100644 index 00000000..b5c6ae5c --- /dev/null +++ b/libraries/tests/e2e/functions/idempotency/AOT-Function/src/AOT-Function/AOT-Function.csproj @@ -0,0 +1,33 @@ + + + Exe + net8.0 + enable + enable + Lambda + + true + + true + + true + + partial + + + + + + + + + + TestHelper.cs + + + + + + \ No newline at end of file diff --git a/libraries/tests/e2e/functions/idempotency/AOT-Function/src/AOT-Function/Function.cs b/libraries/tests/e2e/functions/idempotency/AOT-Function/src/AOT-Function/Function.cs new file mode 100644 index 00000000..3a4c9beb --- /dev/null +++ b/libraries/tests/e2e/functions/idempotency/AOT-Function/src/AOT-Function/Function.cs @@ -0,0 +1,38 @@ +using Amazon.Lambda.Core; +using Amazon.Lambda.RuntimeSupport; +using System.Text.Json.Serialization; +using Amazon.Lambda.APIGatewayEvents; +using Amazon.Lambda.Serialization.SystemTextJson; +using Helpers; + +namespace AOT_Function; + +public static class Function +{ + private static async Task Main() + { + Func handler = FunctionHandler; + await LambdaBootstrapBuilder.Create(handler, + new SourceGeneratorLambdaJsonSerializer()) + .Build() + .RunAsync(); + } + + public static APIGatewayProxyResponse FunctionHandler(APIGatewayProxyRequest apigwProxyEvent, ILambdaContext context) + { + TestHelper.TestMethod(apigwProxyEvent); + + return new APIGatewayProxyResponse() + { + StatusCode = 200, + Body = apigwProxyEvent.Body.ToUpper() + }; + } +} + +[JsonSerializable(typeof(APIGatewayProxyRequest))] +[JsonSerializable(typeof(APIGatewayProxyResponse))] +public partial class LambdaFunctionJsonSerializerContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/libraries/tests/e2e/functions/idempotency/AOT-Function/src/AOT-Function/aws-lambda-tools-defaults.json b/libraries/tests/e2e/functions/idempotency/AOT-Function/src/AOT-Function/aws-lambda-tools-defaults.json new file mode 100644 index 00000000..be3c7ec1 --- /dev/null +++ b/libraries/tests/e2e/functions/idempotency/AOT-Function/src/AOT-Function/aws-lambda-tools-defaults.json @@ -0,0 +1,16 @@ +{ + "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": "AOT-Function", + "msbuild-parameters": "--self-contained true" +} \ No newline at end of file diff --git a/libraries/tests/e2e/functions/idempotency/Function/src/Function/Function.cs b/libraries/tests/e2e/functions/idempotency/Function/src/Function/Function.cs new file mode 100644 index 00000000..672ffdfb --- /dev/null +++ b/libraries/tests/e2e/functions/idempotency/Function/src/Function/Function.cs @@ -0,0 +1,73 @@ +using Amazon.Lambda.APIGatewayEvents; +using Amazon.Lambda.Core; +using AWS.Lambda.Powertools.Idempotency; +using Helpers; + +// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class. +[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))] + +namespace Function +{ + public class Function + { + public Function() + { + var tableName = Environment.GetEnvironmentVariable("IDEMPOTENCY_TABLE_NAME"); + Idempotency.Configure(builder => builder.UseDynamoDb(tableName)); + } + + [Idempotent] + public APIGatewayProxyResponse FunctionHandler(APIGatewayProxyRequest apigwProxyEvent, ILambdaContext context) + { + return TestHelper.TestMethod(apigwProxyEvent); + } + } +} + +namespace IdempotencyAttributeTest +{ + public class Function + { + public Function() + { + var tableName = Environment.GetEnvironmentVariable("IDEMPOTENCY_TABLE_NAME"); + Idempotency.Configure(builder => builder.UseDynamoDb(tableName)); + } + + public APIGatewayProxyResponse FunctionHandler(APIGatewayProxyRequest apigwProxyEvent, ILambdaContext context) + { + return new APIGatewayProxyResponse + { + Body = MyInternalMethod("dummy", apigwProxyEvent.RequestContext.RequestId), + StatusCode = 200 + }; + } + + [Idempotent] + private string MyInternalMethod(string argOne, [IdempotencyKey] string argTwo) { + return Guid.NewGuid().ToString(); + } + } +} + +namespace IdempotencyPayloadSubsetTest +{ + public class Function + { + public Function() + { + var tableName = Environment.GetEnvironmentVariable("IDEMPOTENCY_TABLE_NAME"); + Idempotency.Configure(builder => + builder + .WithOptions(optionsBuilder => + optionsBuilder.WithEventKeyJmesPath("powertools_json(Body).[\"user_id\", \"product_id\"]")) + .UseDynamoDb(tableName)); + } + + [Idempotent] + public APIGatewayProxyResponse FunctionHandler(APIGatewayProxyRequest apigwProxyEvent, ILambdaContext context) + { + return TestHelper.TestMethod(apigwProxyEvent); + } + } +} diff --git a/libraries/tests/e2e/functions/idempotency/Function/src/Function/Function.csproj b/libraries/tests/e2e/functions/idempotency/Function/src/Function/Function.csproj new file mode 100644 index 00000000..0dedeaea --- /dev/null +++ b/libraries/tests/e2e/functions/idempotency/Function/src/Function/Function.csproj @@ -0,0 +1,21 @@ + + + net6.0;net8.0 + enable + enable + true + Lambda + + true + + true + + + + + + + + + + \ No newline at end of file diff --git a/libraries/tests/e2e/functions/idempotency/Function/src/Function/TestHelper.cs b/libraries/tests/e2e/functions/idempotency/Function/src/Function/TestHelper.cs new file mode 100644 index 00000000..4708d225 --- /dev/null +++ b/libraries/tests/e2e/functions/idempotency/Function/src/Function/TestHelper.cs @@ -0,0 +1,35 @@ +using System.Text.Json; +using Amazon.Lambda.APIGatewayEvents; + +namespace Helpers; + +public static class TestHelper +{ + public static APIGatewayProxyResponse TestMethod(APIGatewayProxyRequest apigwProxyEvent) + { + var response = new + { + Greeting = "Hello Powertools for AWS Lambda (.NET)", + Guid = Guid.NewGuid().ToString() // Guid generated in the Handler. used to compare Handler output + }; + + try + { + return new APIGatewayProxyResponse + { + Body = JsonSerializer.Serialize(response), + StatusCode = 200, + Headers = new Dictionary { { "Content-Type", "application/json" } } + }; + } + catch (Exception e) + { + return new APIGatewayProxyResponse + { + Body = e.Message, + StatusCode = 500, + Headers = new Dictionary { { "Content-Type", "application/json" } } + }; + } + } +} \ No newline at end of file diff --git a/libraries/tests/e2e/functions/idempotency/Function/src/Function/aws-lambda-tools-defaults.json b/libraries/tests/e2e/functions/idempotency/Function/src/Function/aws-lambda-tools-defaults.json new file mode 100644 index 00000000..307a7dca --- /dev/null +++ b/libraries/tests/e2e/functions/idempotency/Function/src/Function/aws-lambda-tools-defaults.json @@ -0,0 +1,13 @@ +{ + "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-memory-size": 512, + "function-timeout": 30 +} \ No newline at end of file diff --git a/libraries/tests/e2e/functions/idempotency/Function/test/Function.Tests/Function.Tests.csproj b/libraries/tests/e2e/functions/idempotency/Function/test/Function.Tests/Function.Tests.csproj new file mode 100644 index 00000000..ed863946 --- /dev/null +++ b/libraries/tests/e2e/functions/idempotency/Function/test/Function.Tests/Function.Tests.csproj @@ -0,0 +1,24 @@ + + + net8.0 + enable + enable + true + Logging.E2E.Tests + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libraries/tests/e2e/functions/idempotency/Function/test/Function.Tests/FunctionTests.cs b/libraries/tests/e2e/functions/idempotency/Function/test/Function.Tests/FunctionTests.cs new file mode 100644 index 00000000..e1c4957d --- /dev/null +++ b/libraries/tests/e2e/functions/idempotency/Function/test/Function.Tests/FunctionTests.cs @@ -0,0 +1,300 @@ +using System.Text.Json; +using Amazon.DynamoDBv2; +using Amazon.DynamoDBv2.Model; +using Amazon.Lambda; +using Amazon.Lambda.APIGatewayEvents; +using Xunit; +using Amazon.Lambda.Model; +using Xunit.Abstractions; + +namespace Function.Tests; + +[Trait("Category", "E2E")] +public class FunctionTests +{ + private readonly ITestOutputHelper _testOutputHelper; + private readonly AmazonLambdaClient _lambdaClient; + private readonly AmazonDynamoDBClient _dynamoDbClient; + private string _tableName = null!; + private string _handler; + + public FunctionTests(ITestOutputHelper testOutputHelper) + { + _testOutputHelper = testOutputHelper; + _lambdaClient = new AmazonLambdaClient(); + _dynamoDbClient = new AmazonDynamoDBClient(); + } + + // [Trait("Category", "AOT")] + // [Theory] + // [MemberData(nameof(TestDataAot.Inline), MemberType = typeof(TestDataAot))] + // public async Task IdempotencyHandlerAotTest(string functionName, string tableName) + // { + // _tableName = tableName; + // await TestIdempotencyHandler(functionName); + // } + + [Theory] + [MemberData(nameof(TestData.Inline), MemberType = typeof(TestData))] + public async Task IdempotencyPayloadSubsetTest(string functionName, string tableName) + { + _tableName = tableName; + await UpdateFunctionHandler(functionName, "Function::IdempotencyPayloadSubsetTest.Function::FunctionHandler"); + + // First unique request + var firstProductId = Guid.NewGuid().ToString(); + var (firstResponse1, firstGuid1) = await ExecutePayloadSubsetRequest(functionName, "xyz", firstProductId); + var (firstResponse2, firstGuid2) = await ExecutePayloadSubsetRequest(functionName, "xyz", firstProductId); + + // Assert first request pair + Assert.Equal(200, firstResponse1.StatusCode); + Assert.Equal(200, firstResponse2.StatusCode); + Assert.Equal(firstGuid1, firstGuid2); // Idempotency check + await AssertDynamoDbData( + $"{functionName}.FunctionHandler#{Helpers.HashRequest($"[\"xyz\",\"{firstProductId}\"]")}", + firstGuid1); + + // Second unique request + var secondProductId = Guid.NewGuid().ToString(); + var (secondResponse1, secondGuid1) = await ExecutePayloadSubsetRequest(functionName, "xyz", secondProductId); + var (secondResponse2, secondGuid2) = await ExecutePayloadSubsetRequest(functionName, "xyz", secondProductId); + + // Assert second request pair + Assert.Equal(200, secondResponse1.StatusCode); + Assert.Equal(200, secondResponse2.StatusCode); + Assert.Equal(secondGuid1, secondGuid2); // Idempotency check + Assert.NotEqual(firstGuid1, secondGuid1); // Different requests should have different GUIDs + await AssertDynamoDbData( + $"{functionName}.FunctionHandler#{Helpers.HashRequest($"[\"xyz\",\"{secondProductId}\"]")}", + secondGuid1); + } + + [Theory] + [MemberData(nameof(TestData.Inline), MemberType = typeof(TestData))] + public async Task IdempotencyAttributeTest(string functionName, string tableName) + { + _tableName = tableName; + await UpdateFunctionHandler(functionName, "Function::IdempotencyAttributeTest.Function::FunctionHandler"); + + // First unique request + var requestId1 = Guid.NewGuid().ToString(); + var (firstResponse1, firstGuid1) = await ExecuteAttributeRequest(functionName, requestId1); + var (firstResponse2, firstGuid2) = await ExecuteAttributeRequest(functionName, requestId1); + + // Assert first request pair + Assert.Equal(200, firstResponse1.StatusCode); + Assert.Equal(200, firstResponse2.StatusCode); + Assert.Equal(firstGuid1, firstGuid2); // Idempotency check + await AssertDynamoDbData( + $"{functionName}.MyInternalMethod#{Helpers.HashRequest(requestId1)}", + firstGuid1, + true); + + // Second unique request + var requestId2 = Guid.NewGuid().ToString(); + var (secondResponse1, secondGuid1) = await ExecuteAttributeRequest(functionName, requestId2); + var (secondResponse2, secondGuid2) = await ExecuteAttributeRequest(functionName, requestId2); + + // Assert second request pair + Assert.Equal(200, secondResponse1.StatusCode); + Assert.Equal(200, secondResponse2.StatusCode); + Assert.Equal(secondGuid1, secondGuid2); // Idempotency check + Assert.NotEqual(firstGuid1, secondGuid1); // Different requests should have different GUIDs + await AssertDynamoDbData( + $"{functionName}.MyInternalMethod#{Helpers.HashRequest(requestId2)}", + secondGuid1, + true); + } + + [Theory] + [MemberData(nameof(TestData.Inline), MemberType = typeof(TestData))] + public async Task IdempotencyHandlerTest(string functionName, string tableName) + { + _tableName = tableName; + await UpdateFunctionHandler(functionName, "Function::Function.Function::FunctionHandler"); + + var payload = await File.ReadAllTextAsync("../../../../../../../payload.json"); + + // Execute three identical requests + var (response1, guid1) = await ExecuteHandlerRequest(functionName, payload); + var (response2, guid2) = await ExecuteHandlerRequest(functionName, payload); + var (response3, guid3) = await ExecuteHandlerRequest(functionName, payload); + + // Assert all responses + Assert.Equal(200, response1.StatusCode); + Assert.Equal(200, response2.StatusCode); + Assert.Equal(200, response3.StatusCode); + + // Assert idempotency + Assert.Equal(guid1, guid2); + Assert.Equal(guid2, guid3); + + // Assert DynamoDB + await AssertDynamoDbData( + $"{functionName}.FunctionHandler#35973cf447e6cc11008d603c791a232f", + guid1); + } + + private async Task UpdateFunctionHandler(string functionName, string handler) + { + var updateRequest = new UpdateFunctionConfigurationRequest + { + FunctionName = functionName, + Handler = handler + }; + + var updateResponse = await _lambdaClient.UpdateFunctionConfigurationAsync(updateRequest); + + if (updateResponse.HttpStatusCode == System.Net.HttpStatusCode.OK) + { + Console.WriteLine($"Successfully updated the handler for function {functionName} to {handler}"); + } + else + { + Assert.Fail( + $"Failed to update the handler for function {functionName}. Status code: {updateResponse.HttpStatusCode}"); + } + + //wait a few seconds for the changes to take effect + await Task.Delay(5000); + } + + private async Task AssertDynamoDbData(string id, string requestId, bool isSavedDataString = false) + { + _testOutputHelper.WriteLine($"Querying DynamoDB with id: {id}"); + + var queryRequest = new QueryRequest + { + TableName = _tableName, + KeyConditionExpression = "id = :v_id", + ExpressionAttributeValues = new Dictionary + { + { ":v_id", new AttributeValue { S = id } } + } + }; + + _testOutputHelper.WriteLine($"QueryRequest: {JsonSerializer.Serialize(queryRequest)}"); + + var queryResponse = await _dynamoDbClient.QueryAsync(queryRequest); + + _testOutputHelper.WriteLine($"QueryResponse: {JsonSerializer.Serialize(queryResponse)}"); + + if (queryResponse.Items.Count == 0) + { + Assert.Fail("No items found in DynamoDB for the given id."); + } + + foreach (var item in queryResponse.Items) + { + var data = item["data"].S; + var status = item["status"].S; + + Assert.Equal("COMPLETED", status); + + if (!isSavedDataString) + { + var parsedData = JsonSerializer.Deserialize(data); + + if (parsedData == null) + { + Assert.Fail("Failed to parse data field."); + } + + var parsedResponse = JsonSerializer.Deserialize(parsedData.Body); + if (parsedResponse == null) + { + Assert.Fail("Failed to parse response."); + } + + Assert.Equal(requestId, parsedResponse.Guid); + } + else + { + Assert.Equal(requestId, data.Trim('"')); + } + } + } + + // Helper methods for executing requests + private async Task<(APIGatewayProxyResponse Response, string Guid)> ExecutePayloadSubsetRequest( + string functionName, string userId, string productId) + { + var request = new InvokeRequest + { + FunctionName = functionName, + InvocationType = InvocationType.RequestResponse, + Payload = JsonSerializer.Serialize(new APIGatewayProxyRequest + { + Body = $"{{\"user_id\":\"{userId}\",\"product_id\":\"{productId}\"}}" + }), + LogType = LogType.Tail + }; + + return await ExecuteRequest(request); + } + + private async Task<(APIGatewayProxyResponse Response, string Guid)> ExecuteAttributeRequest( + string functionName, string requestId) + { + var request = new InvokeRequest + { + FunctionName = functionName, + InvocationType = InvocationType.RequestResponse, + Payload = JsonSerializer.Serialize(new APIGatewayProxyRequest + { + Body = "{\"user_id\":\"***\",\"product_id\":\"123456789\"}", + RequestContext = new APIGatewayProxyRequest.ProxyRequestContext + { + AccountId = "123456789012", + RequestId = requestId + } + }), + LogType = LogType.Tail + }; + + return await ExecuteRequest(request); + } + + private async Task<(APIGatewayProxyResponse Response, string Guid)> ExecuteHandlerRequest( + string functionName, string payload) + { + var request = new InvokeRequest + { + FunctionName = functionName, + InvocationType = InvocationType.RequestResponse, + Payload = payload, + LogType = LogType.Tail + }; + + return await ExecuteRequest(request); + } + + private async Task<(APIGatewayProxyResponse Response, string Guid)> ExecuteRequest(InvokeRequest request) + { + var response = await _lambdaClient.InvokeAsync(request); + + if (string.IsNullOrEmpty(response.LogResult)) + Assert.Fail("No LogResult field returned in the response of Lambda invocation."); + + var responsePayload = System.Text.Encoding.UTF8.GetString(response.Payload.ToArray()); + var parsedResponse = JsonSerializer.Deserialize(responsePayload) + ?? throw new Exception("Failed to parse payload."); + + string guid; + try + { + // The GUID is inside the Response object + var parsedBody = JsonSerializer.Deserialize(parsedResponse.Body); + guid = parsedBody?.Guid ?? parsedResponse.Body; + } + catch (JsonException) + { + // For scenarios where the Body is already the GUID + guid = parsedResponse.Body; + } + + return (parsedResponse, guid); + } +} + +public record Response(string Greeting, string Guid); \ No newline at end of file diff --git a/libraries/tests/e2e/functions/idempotency/Function/test/Function.Tests/Helpers.cs b/libraries/tests/e2e/functions/idempotency/Function/test/Function.Tests/Helpers.cs new file mode 100644 index 00000000..ca270bbf --- /dev/null +++ b/libraries/tests/e2e/functions/idempotency/Function/test/Function.Tests/Helpers.cs @@ -0,0 +1,25 @@ +using System.Security.Cryptography; +using System.Text; + +namespace Function.Tests; + +public static class Helpers +{ + public static string HashRequest(string input) + { + using var hashAlgorithm = MD5.Create(); + if (hashAlgorithm == null) + { + throw new ArgumentException("Invalid HashAlgorithm"); + } + + var data = hashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(input)); + var sBuilder = new StringBuilder(); + for (var i = 0; i < data.Length; i++) + { + sBuilder.Append(data[i].ToString("x2")); + } + + return sBuilder.ToString(); + } +} \ No newline at end of file diff --git a/libraries/tests/e2e/functions/idempotency/Function/test/Function.Tests/TestData.cs b/libraries/tests/e2e/functions/idempotency/Function/test/Function.Tests/TestData.cs new file mode 100644 index 00000000..f4967b7d --- /dev/null +++ b/libraries/tests/e2e/functions/idempotency/Function/test/Function.Tests/TestData.cs @@ -0,0 +1,23 @@ +namespace Function.Tests; + +public static class TestData +{ + public static IEnumerable Inline => + new List + { + new object[] { "E2ETestLambda_X64_NET6_idempotency", "IdempotencyTable" }, + new object[] { "E2ETestLambda_ARM_NET6_idempotency", "IdempotencyTable" }, + new object[] { "E2ETestLambda_X64_NET8_idempotency", "IdempotencyTable" }, + new object[] { "E2ETestLambda_ARM_NET8_idempotency", "IdempotencyTable" } + }; +} + +public static class TestDataAot +{ + public static IEnumerable Inline => + new List + { + new object[] { "E2ETestLambda_X64_AOT_NET8_idempotency", "IdempotencyTable-AOT-x86_64" }, + new object[] { "E2ETestLambda_ARM_AOT_NET8_idempotency", "IdempotencyTable-AOT-arm64" } + }; +} \ No newline at end of file diff --git a/libraries/tests/e2e/functions/idempotency/IdempotencyTests.sln b/libraries/tests/e2e/functions/idempotency/IdempotencyTests.sln new file mode 100644 index 00000000..add4ad93 --- /dev/null +++ b/libraries/tests/e2e/functions/idempotency/IdempotencyTests.sln @@ -0,0 +1,30 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Function", "Function", "{FE3A26C9-5A8D-4DD3-A87B-2D7FC5BC15A8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{28C61FF3-B4F5-44AC-9375-A4C6FC8579C8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Function.Tests", "Function\test\Function.Tests\Function.Tests.csproj", "{8959B0AC-3B85-4E30-9C48-CAD5F72AD5BB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8959B0AC-3B85-4E30-9C48-CAD5F72AD5BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8959B0AC-3B85-4E30-9C48-CAD5F72AD5BB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8959B0AC-3B85-4E30-9C48-CAD5F72AD5BB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8959B0AC-3B85-4E30-9C48-CAD5F72AD5BB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {28C61FF3-B4F5-44AC-9375-A4C6FC8579C8} = {FE3A26C9-5A8D-4DD3-A87B-2D7FC5BC15A8} + {8959B0AC-3B85-4E30-9C48-CAD5F72AD5BB} = {28C61FF3-B4F5-44AC-9375-A4C6FC8579C8} + EndGlobalSection +EndGlobal diff --git a/libraries/tests/e2e/functions/core/payload.json b/libraries/tests/e2e/functions/payload.json similarity index 100% rename from libraries/tests/e2e/functions/core/payload.json rename to libraries/tests/e2e/functions/payload.json diff --git a/libraries/tests/e2e/infra-aot/AOTStackProps.cs b/libraries/tests/e2e/infra-aot/AOTStackProps.cs deleted file mode 100644 index 4e735a8f..00000000 --- a/libraries/tests/e2e/infra-aot/AOTStackProps.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Amazon.CDK; - -namespace InfraAot; - -public class AotStackProps : StackProps -{ - public string Architecture { get; set; } -} \ No newline at end of file diff --git a/libraries/tests/e2e/infra-aot/CoreAotStack.cs b/libraries/tests/e2e/infra-aot/CoreAotStack.cs index 40197ab5..4387892c 100644 --- a/libraries/tests/e2e/infra-aot/CoreAotStack.cs +++ b/libraries/tests/e2e/infra-aot/CoreAotStack.cs @@ -1,7 +1,7 @@ using Amazon.CDK; using Amazon.CDK.AWS.Lambda; using Constructs; -using TestUtils; +using InfraShared; using Architecture = Amazon.CDK.AWS.Lambda.Architecture; namespace InfraAot; @@ -10,9 +10,9 @@ public class CoreAotStack : Stack { private readonly Architecture _architecture; - internal CoreAotStack(Construct scope, string id, AotStackProps props = null) : base(scope, id, props) + internal CoreAotStack(Construct scope, string id, PowertoolsDefaultStackProps props = null) : base(scope, id, props) { - if (props != null) _architecture = props.Architecture == "arm64" ? Architecture.ARM_64 : Architecture.X86_64; + if (props != null) _architecture = props.ArchitectureString == "arm64" ? Architecture.ARM_64 : Architecture.X86_64; CreateFunctionConstructs("logging"); CreateFunctionConstructs("metrics"); @@ -25,7 +25,7 @@ private void CreateFunctionConstructs(string utility) var distAotPath = $"../functions/core/{utility}/AOT-Function/dist"; var arch = _architecture == Architecture.X86_64 ? "X64" : "ARM"; - CreateFunctionConstruct(this, $"{utility}_ARM_aot_net8", Runtime.DOTNET_8, _architecture, + CreateFunctionConstruct(this, $"{utility}_{arch}_aot_net8", Runtime.DOTNET_8, _architecture, $"E2ETestLambda_{arch}_AOT_NET8_{utility}", baseAotPath, distAotPath); } @@ -39,7 +39,8 @@ private void CreateFunctionConstruct(Construct scope, string id, Runtime runtime Name = name, Handler = "AOT-Function", SourcePath = sourcePath, - DistPath = distPath + DistPath = distPath, + IsAot = true }); } } \ No newline at end of file diff --git a/libraries/tests/e2e/infra-aot/FunctionConstruct.cs b/libraries/tests/e2e/infra-aot/FunctionConstruct.cs deleted file mode 100644 index aa4e92cb..00000000 --- a/libraries/tests/e2e/infra-aot/FunctionConstruct.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Collections.Generic; -using Amazon.CDK; -using Amazon.CDK.AWS.Lambda; -using Constructs; -using TestUtils; - -namespace InfraAot; - -public class FunctionConstruct : Construct -{ - public FunctionConstruct(Construct scope, string id, FunctionConstructProps props) : base(scope, id) - { - var distPath = $"{props.DistPath}/deploy_{props.Architecture.Name}_{props.Runtime.Name}.zip"; - _ = new Function(this, id, new FunctionProps - { - Runtime = props.Runtime, - Architecture = props.Architecture, - FunctionName = props.Name, - Handler = props.Handler, - Tracing = Tracing.ACTIVE, - Timeout = Duration.Seconds(10), - Code = Code.FromCustomCommand(distPath, - [ - $"dotnet-lambda package -pl {props.SourcePath} -cmd ../../../ -o {distPath} -f net8.0 -farch {props.Architecture.Name} -cifb public.ecr.aws/sam/build-dotnet8" - ], - new CustomCommandOptions - { - CommandOptions = new Dictionary {{"shell", true }} - }) - }); - } -} \ No newline at end of file diff --git a/libraries/tests/e2e/infra-aot/InfraAot.csproj b/libraries/tests/e2e/infra-aot/InfraAot.csproj index e1c7a0f9..1e7b2b1e 100644 --- a/libraries/tests/e2e/infra-aot/InfraAot.csproj +++ b/libraries/tests/e2e/infra-aot/InfraAot.csproj @@ -18,5 +18,6 @@ + diff --git a/libraries/tests/e2e/infra-aot/Program.cs b/libraries/tests/e2e/infra-aot/Program.cs index d50d0f5f..db4d533a 100644 --- a/libraries/tests/e2e/infra-aot/Program.cs +++ b/libraries/tests/e2e/infra-aot/Program.cs @@ -1,4 +1,5 @@ using Amazon.CDK; +using InfraShared; namespace InfraAot { @@ -11,7 +12,8 @@ public static void Main(string[] args) var architecture = app.Node.TryGetContext("architecture")?.ToString(); if (architecture == null) { - throw new System.ArgumentException("architecture context is required. Please provide it with --context architecture=arm64|x86_64"); + throw new System.ArgumentException( + "architecture context is required. Please provide it with --context architecture=arm64|x86_64"); } if (architecture != "arm64" && architecture != "x86_64") @@ -20,16 +22,22 @@ public static void Main(string[] args) } var id = "CoreAotStack"; - if(architecture == "arm64") + var idempotencystackAotId = "IdempotencyStack-AOT"; + if (architecture == "arm64") { id = $"CoreAotStack-{architecture}"; + idempotencystackAotId = $"IdempotencyStack-AOT-{architecture}"; } - _ = new CoreAotStack(app, id, new AotStackProps + _ = new CoreAotStack(app, id, new PowertoolsDefaultStackProps { - Architecture = architecture + ArchitectureString = architecture }); + + _ = new IdempotencyStack(app, idempotencystackAotId, + new IdempotencyStackProps { IsAot = true, ArchitectureString = architecture, TableName = $"IdempotencyTable-AOT-{architecture}" }); + app.Synth(); } } -} +} \ No newline at end of file diff --git a/libraries/tests/e2e/infra/CoreStack.cs b/libraries/tests/e2e/infra/CoreStack.cs index 23455b64..d77c725a 100644 --- a/libraries/tests/e2e/infra/CoreStack.cs +++ b/libraries/tests/e2e/infra/CoreStack.cs @@ -1,7 +1,7 @@ using Amazon.CDK; using Amazon.CDK.AWS.Lambda; using Constructs; -using TestUtils; +using InfraShared; using Architecture = Amazon.CDK.AWS.Lambda.Architecture; namespace Infra @@ -40,7 +40,7 @@ private void CreateFunctionConstruct(Construct scope, string id, Runtime runtime Name = name, Handler = "Function::Function.Function::FunctionHandler", SourcePath = sourcePath, - DistPath = distPath + DistPath = distPath, }); } } diff --git a/libraries/tests/e2e/infra/Infra.csproj b/libraries/tests/e2e/infra/Infra.csproj index e1c7a0f9..9b7b55f6 100644 --- a/libraries/tests/e2e/infra/Infra.csproj +++ b/libraries/tests/e2e/infra/Infra.csproj @@ -17,6 +17,6 @@ - + diff --git a/libraries/tests/e2e/infra/Program.cs b/libraries/tests/e2e/infra/Program.cs index 6d1267c8..d56d83b2 100644 --- a/libraries/tests/e2e/infra/Program.cs +++ b/libraries/tests/e2e/infra/Program.cs @@ -1,4 +1,5 @@ using Amazon.CDK; +using InfraShared; namespace Infra { @@ -10,6 +11,8 @@ public static void Main(string[] args) _ = new CoreStack(app, "CoreStack", new StackProps { }); + _ = new IdempotencyStack(app, "IdempotencyStack", new IdempotencyStackProps { TableName = "IdempotencyTable" }); + app.Synth(); } }