From 2acfb6686cd65bddd9551dc8c30d986a877432b8 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Mon, 6 Nov 2023 09:34:51 +0000 Subject: [PATCH 1/2] add e2e tests for idempotency method --- .../IdempotencyFunctionMethodDecorated.cs | 99 +++++++++++++++++++ .../IdempotencyTest.cs | 44 ++++++++- .../Internal/IdempotentAspectTests.cs | 2 +- 3 files changed, 141 insertions(+), 4 deletions(-) create mode 100644 libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Handlers/IdempotencyFunctionMethodDecorated.cs diff --git a/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Handlers/IdempotencyFunctionMethodDecorated.cs b/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Handlers/IdempotencyFunctionMethodDecorated.cs new file mode 100644 index 00000000..4a404588 --- /dev/null +++ b/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Handlers/IdempotencyFunctionMethodDecorated.cs @@ -0,0 +1,99 @@ +/* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Text.Json; +using System.Threading.Tasks; +using Amazon.DynamoDBv2; +using Amazon.Lambda.APIGatewayEvents; +using Amazon.Lambda.Core; + +namespace AWS.Lambda.Powertools.Idempotency.Tests.Handlers; + +public class IdempotencyFunctionMethodDecorated +{ + public bool MethodCalled; + + public IdempotencyFunctionMethodDecorated(AmazonDynamoDBClient client) + { + Idempotency.Configure(builder => + builder + .UseDynamoDb(storeBuilder => + storeBuilder + .WithTableName("idempotency_table") + .WithDynamoDBClient(client) + )); + } + + + public async Task Handle(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context) + { + Idempotency.RegisterLambdaContext(context); + var result= await InternalFunctionHandler(apigProxyEvent); + + return result; + } + + private async Task InternalFunctionHandler(APIGatewayProxyRequest apigProxyEvent) + { + Dictionary headers = new() + { + {"Content-Type", "application/json"}, + {"Access-Control-Allow-Origin", "*"}, + {"Access-Control-Allow-Methods", "GET, OPTIONS"}, + {"Access-Control-Allow-Headers", "*"} + }; + + try + { + var address = JsonDocument.Parse(apigProxyEvent.Body).RootElement.GetProperty("address").GetString(); + var pageContents = await GetPageContents(address); + var output = $"{{ \"message\": \"hello world\", \"location\": \"{pageContents}\" }}"; + + return new APIGatewayProxyResponse + { + Body = output, + StatusCode = 200, + Headers = headers + }; + + } + catch (IOException) + { + return new APIGatewayProxyResponse + { + Body = "{}", + StatusCode = 500, + Headers = headers + }; + } + } + + [Idempotent] + private async Task GetPageContents(string address) + { + MethodCalled = true; + + var client = new HttpClient(); + using var response = await client.GetAsync(address); + using var content = response.Content; + var pageContent = await content.ReadAsStringAsync(); + + return pageContent; + } +} \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/IdempotencyTest.cs b/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/IdempotencyTest.cs index c0170682..aeed0d15 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/IdempotencyTest.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/IdempotencyTest.cs @@ -1,24 +1,26 @@ /* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. - * + * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at - * + * * http://aws.amazon.com/apache2.0 - * + * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ +using System; using System.IO; using System.Text.Json; using System.Threading.Tasks; using Amazon.DynamoDBv2; using Amazon.DynamoDBv2.Model; using Amazon.Lambda.APIGatewayEvents; +using Amazon.Lambda.TestUtilities; using AWS.Lambda.Powertools.Idempotency.Tests.Handlers; using AWS.Lambda.Powertools.Idempotency.Tests.Persistence; using FluentAssertions; @@ -67,4 +69,40 @@ public async Task EndToEndTest() }); scanResponse.Count.Should().Be(1); } + + [Fact] + [Trait("Category", "Integration")] + public async Task EndToEndTestMethod() + { + var function = new IdempotencyFunctionMethodDecorated(_client); + + var options = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }; + + var context = new TestLambdaContext + { + RemainingTime = TimeSpan.FromSeconds(30) + }; + + var request = JsonSerializer.Deserialize(await File.ReadAllTextAsync("./resources/apigw_event2.json"),options); + + var response = await function.Handle(request, context); + function.MethodCalled.Should().BeTrue(); + + function.MethodCalled = false; + + var response2 = await function.Handle(request, context); + function.MethodCalled.Should().BeFalse(); + + JsonSerializer.Serialize(response).Should().Be(JsonSerializer.Serialize(response)); + response2.Body.Should().Contain("hello world"); + + var scanResponse = await _client.ScanAsync(new ScanRequest + { + TableName = _tableName + }); + scanResponse.Count.Should().Be(1); + } } \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Internal/IdempotentAspectTests.cs b/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Internal/IdempotentAspectTests.cs index c5eda8c2..9856ee78 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Internal/IdempotentAspectTests.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Internal/IdempotentAspectTests.cs @@ -310,7 +310,7 @@ public void Handle_WhenIdempotencyOnSubMethodAnnotated_AndSecondCall_AndNotExpir { // Arrange var store = Substitute.For(); - store.SaveInProgress(Arg.Any(), Arg.Any(), Arg.Any()) + store.SaveInProgress(Arg.Any(), Arg.Any(), null) .Throws(new IdempotencyItemAlreadyExistsException()); Idempotency.Configure(builder => builder.WithPersistenceStore(store)); From a87e49278b0a655ff02f6fa020705414bf897f36 Mon Sep 17 00:00:00 2001 From: Henrique Graca <999396+hjgraca@users.noreply.github.com> Date: Tue, 21 Nov 2023 15:28:48 +0000 Subject: [PATCH 2/2] fix tests --- .../IdempotencyTest.cs | 19 ++++++++++++++++++- .../Internal/IdempotentAspectTests.cs | 6 +++--- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/IdempotencyTest.cs b/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/IdempotencyTest.cs index aeed0d15..018e672a 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/IdempotencyTest.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/IdempotencyTest.cs @@ -14,6 +14,7 @@ */ using System; +using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Threading.Tasks; @@ -68,6 +69,13 @@ public async Task EndToEndTest() TableName = _tableName }); scanResponse.Count.Should().Be(1); + + // delete row dynamo + var key = new Dictionary + { + ["id"] = new AttributeValue { S = "testFunction.GetPageContents#ff323c6f0c5ceb97eed49121babcec0f" } + }; + await _client.DeleteItemAsync(new DeleteItemRequest{TableName = _tableName, Key = key}); } [Fact] @@ -96,7 +104,9 @@ public async Task EndToEndTestMethod() var response2 = await function.Handle(request, context); function.MethodCalled.Should().BeFalse(); - JsonSerializer.Serialize(response).Should().Be(JsonSerializer.Serialize(response)); + // Assert + JsonSerializer.Serialize(response).Should().Be(JsonSerializer.Serialize(response2)); + response.Body.Should().Contain("hello world"); response2.Body.Should().Contain("hello world"); var scanResponse = await _client.ScanAsync(new ScanRequest @@ -104,5 +114,12 @@ public async Task EndToEndTestMethod() TableName = _tableName }); scanResponse.Count.Should().Be(1); + + // delete row dynamo + var key = new Dictionary + { + ["id"] = new AttributeValue { S = "testFunction.GetPageContents#ff323c6f0c5ceb97eed49121babcec0f" } + }; + await _client.DeleteItemAsync(new DeleteItemRequest{TableName = _tableName, Key = key}); } } \ No newline at end of file diff --git a/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Internal/IdempotentAspectTests.cs b/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Internal/IdempotentAspectTests.cs index 9856ee78..d9836854 100644 --- a/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Internal/IdempotentAspectTests.cs +++ b/libraries/tests/AWS.Lambda.Powertools.Idempotency.Tests/Internal/IdempotentAspectTests.cs @@ -310,8 +310,8 @@ public void Handle_WhenIdempotencyOnSubMethodAnnotated_AndSecondCall_AndNotExpir { // Arrange var store = Substitute.For(); - store.SaveInProgress(Arg.Any(), Arg.Any(), null) - .Throws(new IdempotencyItemAlreadyExistsException()); + store.SaveInProgress(Arg.Any(), Arg.Any(), Arg.Any()) + .Returns(_ => throw new IdempotencyItemAlreadyExistsException()); Idempotency.Configure(builder => builder.WithPersistenceStore(store)); @@ -327,7 +327,7 @@ public void Handle_WhenIdempotencyOnSubMethodAnnotated_AndSecondCall_AndNotExpir .Returns(record); // Act - var function = new IdempotencyInternalFunction(false); + var function = new IdempotencyInternalFunction(true); Basket resultBasket = function.HandleRequest(product, new TestLambdaContext()); // assert