Skip to content

Commit 2c1f193

Browse files
committed
Removed nested for loops and replaced them with explicit test cases. Created helper methods for executing requests to reduce code duplication.Made the test flow more linear and easier to follow
1 parent 466510c commit 2c1f193

File tree

1 file changed

+163
-202
lines changed
  • libraries/tests/e2e/functions/idempotency/Function/test/Function.Tests

1 file changed

+163
-202
lines changed

libraries/tests/e2e/functions/idempotency/Function/test/Function.Tests/FunctionTests.cs

Lines changed: 163 additions & 202 deletions
Original file line numberDiff line numberDiff line change
@@ -34,225 +34,105 @@ public FunctionTests(ITestOutputHelper testOutputHelper)
3434
// await TestIdempotencyHandler(functionName);
3535
// }
3636

37-
[Theory]
38-
[MemberData(nameof(TestData.Inline), MemberType = typeof(TestData))]
39-
public async Task IdempotencyHandlerTest(string functionName, string tableName)
40-
{
41-
_tableName = tableName;
42-
await TestIdempotencyHandler(functionName);
43-
}
44-
45-
[Theory]
46-
[MemberData(nameof(TestData.Inline), MemberType = typeof(TestData))]
47-
public async Task IdempotencyAttributeTest(string functionName, string tableName)
48-
{
49-
_tableName = tableName;
50-
await TestIdempotencyAttribute(functionName);
51-
}
52-
5337
[Theory]
5438
[MemberData(nameof(TestData.Inline), MemberType = typeof(TestData))]
5539
public async Task IdempotencyPayloadSubsetTest(string functionName, string tableName)
5640
{
5741
_tableName = tableName;
58-
await TestIdempotencyPayloadSubset(functionName);
59-
}
60-
61-
private async Task TestIdempotencyPayloadSubset(string functionName)
62-
{
6342
await UpdateFunctionHandler(functionName, "Function::IdempotencyPayloadSubsetTest.Function::FunctionHandler");
6443

65-
var initialGuid = string.Empty;
66-
67-
for (int i = 0; i < 2; i++)
68-
{
69-
var productId = Guid.NewGuid().ToString();
70-
var apiGatewayRequest = new APIGatewayProxyRequest
71-
{
72-
Body = $"{{\"user_id\":\"xyz\",\"product_id\":\"{productId}\"}}"
73-
};
74-
75-
var payload = JsonSerializer.Serialize(apiGatewayRequest);
76-
77-
var request = new InvokeRequest
78-
{
79-
FunctionName = functionName,
80-
InvocationType = InvocationType.RequestResponse,
81-
Payload = payload,
82-
LogType = LogType.Tail,
83-
};
84-
85-
// run two times with the same request
86-
for (int j = 0; j < 2; j++)
87-
{
88-
var response = await _lambdaClient.InvokeAsync(request);
89-
90-
if (string.IsNullOrEmpty(response.LogResult))
91-
{
92-
Assert.Fail("No LogResult field returned in the response of Lambda invocation.");
93-
}
94-
95-
var responsePayload = System.Text.Encoding.UTF8.GetString(response.Payload.ToArray());
96-
var parsedPayload = JsonSerializer.Deserialize<APIGatewayProxyResponse>(responsePayload);
97-
98-
if (parsedPayload == null)
99-
{
100-
Assert.Fail("Failed to parse payload.");
101-
}
102-
103-
Assert.Equal(200, parsedPayload.StatusCode);
104-
105-
var parsedResponse = JsonSerializer.Deserialize<Response>(parsedPayload.Body);
106-
107-
if (parsedResponse == null)
108-
{
109-
Assert.Fail("Failed to parse response.");
110-
}
111-
112-
if (j == 0)
113-
{
114-
// first call should return a new guid
115-
if (parsedResponse.Guid == initialGuid)
116-
{
117-
Assert.Fail("Idempotency failed to clear cache.");
118-
}
119-
120-
initialGuid = parsedResponse.Guid;
121-
}
122-
123-
Assert.Equal(initialGuid, parsedResponse.Guid);
124-
125-
// Query DynamoDB and assert results
126-
var hashRequest = Helpers.HashRequest($"[\"xyz\",\"{productId}\"]");
127-
128-
var id = $"{functionName}.FunctionHandler#{hashRequest}";
129-
await AssertDynamoDbData(id, initialGuid);
130-
}
131-
}
44+
// First unique request
45+
var firstProductId = Guid.NewGuid().ToString();
46+
var (firstResponse1, firstGuid1) = await ExecutePayloadSubsetRequest(functionName, "xyz", firstProductId);
47+
var (firstResponse2, firstGuid2) = await ExecutePayloadSubsetRequest(functionName, "xyz", firstProductId);
48+
49+
// Assert first request pair
50+
Assert.Equal(200, firstResponse1.StatusCode);
51+
Assert.Equal(200, firstResponse2.StatusCode);
52+
Assert.Equal(firstGuid1, firstGuid2); // Idempotency check
53+
await AssertDynamoDbData(
54+
$"{functionName}.FunctionHandler#{Helpers.HashRequest($"[\"xyz\",\"{firstProductId}\"]")}",
55+
firstGuid1);
56+
57+
// Second unique request
58+
var secondProductId = Guid.NewGuid().ToString();
59+
var (secondResponse1, secondGuid1) = await ExecutePayloadSubsetRequest(functionName, "xyz", secondProductId);
60+
var (secondResponse2, secondGuid2) = await ExecutePayloadSubsetRequest(functionName, "xyz", secondProductId);
61+
62+
// Assert second request pair
63+
Assert.Equal(200, secondResponse1.StatusCode);
64+
Assert.Equal(200, secondResponse2.StatusCode);
65+
Assert.Equal(secondGuid1, secondGuid2); // Idempotency check
66+
Assert.NotEqual(firstGuid1, secondGuid1); // Different requests should have different GUIDs
67+
await AssertDynamoDbData(
68+
$"{functionName}.FunctionHandler#{Helpers.HashRequest($"[\"xyz\",\"{secondProductId}\"]")}",
69+
secondGuid1);
13270
}
13371

134-
private async Task TestIdempotencyAttribute(string functionName)
72+
[Theory]
73+
[MemberData(nameof(TestData.Inline), MemberType = typeof(TestData))]
74+
public async Task IdempotencyAttributeTest(string functionName, string tableName)
13575
{
76+
_tableName = tableName;
13677
await UpdateFunctionHandler(functionName, "Function::IdempotencyAttributeTest.Function::FunctionHandler");
13778

138-
var initialGuid = string.Empty;
139-
140-
for (int i = 0; i < 2; i++)
141-
{
142-
var requestId = Guid.NewGuid().ToString();
143-
var apiGatewayRequest = new APIGatewayProxyRequest
144-
{
145-
Body = "{\"user_id\":\"xyz\",\"product_id\":\"123456789\"}",
146-
RequestContext = new APIGatewayProxyRequest.ProxyRequestContext
147-
{
148-
AccountId = "123456789012",
149-
RequestId = requestId, // requestId is used to invalidate the cache
150-
}
151-
};
152-
153-
var payload = JsonSerializer.Serialize(apiGatewayRequest);
154-
155-
var request = new InvokeRequest
156-
{
157-
FunctionName = functionName,
158-
InvocationType = InvocationType.RequestResponse,
159-
Payload = payload,
160-
LogType = LogType.Tail,
161-
};
162-
163-
// run two times with the same request
164-
for (int j = 0; j < 2; j++)
165-
{
166-
var response = await _lambdaClient.InvokeAsync(request);
167-
168-
if (string.IsNullOrEmpty(response.LogResult))
169-
{
170-
Assert.Fail("No LogResult field returned in the response of Lambda invocation.");
171-
}
172-
173-
var responsePayload = System.Text.Encoding.UTF8.GetString(response.Payload.ToArray());
174-
var parsedPayload = JsonSerializer.Deserialize<APIGatewayProxyResponse>(responsePayload);
175-
176-
if (parsedPayload == null)
177-
{
178-
Assert.Fail("Failed to parse payload.");
179-
}
180-
181-
Assert.Equal(200, parsedPayload.StatusCode);
182-
183-
if (j == 0)
184-
{
185-
// first call should return a new guid
186-
if (parsedPayload.Body == initialGuid)
187-
{
188-
Assert.Fail("Idempotency failed to clear cache.");
189-
}
190-
191-
initialGuid = parsedPayload.Body;
192-
}
193-
194-
Assert.Equal(initialGuid, parsedPayload.Body);
195-
196-
// Query DynamoDB and assert results
197-
var hashRequestId = Helpers.HashRequest(requestId);
198-
var id = $"{functionName}.MyInternalMethod#{hashRequestId}";
199-
await AssertDynamoDbData(id, initialGuid, true);
200-
}
201-
}
79+
// First unique request
80+
var requestId1 = Guid.NewGuid().ToString();
81+
var (firstResponse1, firstGuid1) = await ExecuteAttributeRequest(functionName, requestId1);
82+
var (firstResponse2, firstGuid2) = await ExecuteAttributeRequest(functionName, requestId1);
83+
84+
// Assert first request pair
85+
Assert.Equal(200, firstResponse1.StatusCode);
86+
Assert.Equal(200, firstResponse2.StatusCode);
87+
Assert.Equal(firstGuid1, firstGuid2); // Idempotency check
88+
await AssertDynamoDbData(
89+
$"{functionName}.MyInternalMethod#{Helpers.HashRequest(requestId1)}",
90+
firstGuid1,
91+
true);
92+
93+
// Second unique request
94+
var requestId2 = Guid.NewGuid().ToString();
95+
var (secondResponse1, secondGuid1) = await ExecuteAttributeRequest(functionName, requestId2);
96+
var (secondResponse2, secondGuid2) = await ExecuteAttributeRequest(functionName, requestId2);
97+
98+
// Assert second request pair
99+
Assert.Equal(200, secondResponse1.StatusCode);
100+
Assert.Equal(200, secondResponse2.StatusCode);
101+
Assert.Equal(secondGuid1, secondGuid2); // Idempotency check
102+
Assert.NotEqual(firstGuid1, secondGuid1); // Different requests should have different GUIDs
103+
await AssertDynamoDbData(
104+
$"{functionName}.MyInternalMethod#{Helpers.HashRequest(requestId2)}",
105+
secondGuid1,
106+
true);
202107
}
203108

204-
internal async Task TestIdempotencyHandler(string functionName)
109+
[Theory]
110+
[MemberData(nameof(TestData.Inline), MemberType = typeof(TestData))]
111+
public async Task IdempotencyHandlerTest(string functionName, string tableName)
205112
{
113+
_tableName = tableName;
206114
await UpdateFunctionHandler(functionName, "Function::Function.Function::FunctionHandler");
207115

208-
var request = new InvokeRequest
209-
{
210-
FunctionName = functionName,
211-
InvocationType = InvocationType.RequestResponse,
212-
Payload = await File.ReadAllTextAsync("../../../../../../../payload.json"),
213-
LogType = LogType.Tail,
214-
};
215-
216-
var initialGuid = string.Empty;
217-
218-
// run three times to test idempotency
219-
for (int i = 0; i < 3; i++)
220-
{
221-
var response = await _lambdaClient.InvokeAsync(request);
222-
223-
if (string.IsNullOrEmpty(response.LogResult))
224-
{
225-
Assert.Fail("No LogResult field returned in the response of Lambda invocation.");
226-
}
227-
228-
var payload = System.Text.Encoding.UTF8.GetString(response.Payload.ToArray());
229-
var parsedPayload = JsonSerializer.Deserialize<APIGatewayProxyResponse>(payload);
230-
231-
if (parsedPayload == null)
232-
{
233-
Assert.Fail("Failed to parse payload.");
234-
}
235-
236-
Assert.Equal(200, parsedPayload.StatusCode);
237-
238-
var parsedResponse = JsonSerializer.Deserialize<Response>(parsedPayload.Body);
239-
240-
if (parsedResponse == null)
241-
{
242-
Assert.Fail("Failed to parse response.");
243-
}
244-
245-
if (i == 0)
246-
{
247-
initialGuid = parsedResponse.Guid;
248-
}
249-
250-
Assert.Equal(initialGuid, parsedResponse.Guid);
251-
}
252-
253-
// Query DynamoDB and assert results
254-
var id = $"{functionName}.FunctionHandler#35973cf447e6cc11008d603c791a232f";
255-
await AssertDynamoDbData(id, initialGuid);
116+
var payload = await File.ReadAllTextAsync("../../../../../../../payload.json");
117+
118+
// Execute three identical requests
119+
var (response1, guid1) = await ExecuteHandlerRequest(functionName, payload);
120+
var (response2, guid2) = await ExecuteHandlerRequest(functionName, payload);
121+
var (response3, guid3) = await ExecuteHandlerRequest(functionName, payload);
122+
123+
// Assert all responses
124+
Assert.Equal(200, response1.StatusCode);
125+
Assert.Equal(200, response2.StatusCode);
126+
Assert.Equal(200, response3.StatusCode);
127+
128+
// Assert idempotency
129+
Assert.Equal(guid1, guid2);
130+
Assert.Equal(guid2, guid3);
131+
132+
// Assert DynamoDB
133+
await AssertDynamoDbData(
134+
$"{functionName}.FunctionHandler#35973cf447e6cc11008d603c791a232f",
135+
guid1);
256136
}
257137

258138
private async Task UpdateFunctionHandler(string functionName, string handler)
@@ -334,6 +214,87 @@ private async Task AssertDynamoDbData(string id, string requestId, bool isSavedD
334214
}
335215
}
336216
}
217+
218+
// Helper methods for executing requests
219+
private async Task<(APIGatewayProxyResponse Response, string Guid)> ExecutePayloadSubsetRequest(
220+
string functionName, string userId, string productId)
221+
{
222+
var request = new InvokeRequest
223+
{
224+
FunctionName = functionName,
225+
InvocationType = InvocationType.RequestResponse,
226+
Payload = JsonSerializer.Serialize(new APIGatewayProxyRequest
227+
{
228+
Body = $"{{\"user_id\":\"{userId}\",\"product_id\":\"{productId}\"}}"
229+
}),
230+
LogType = LogType.Tail
231+
};
232+
233+
return await ExecuteRequest(request);
234+
}
235+
236+
private async Task<(APIGatewayProxyResponse Response, string Guid)> ExecuteAttributeRequest(
237+
string functionName, string requestId)
238+
{
239+
var request = new InvokeRequest
240+
{
241+
FunctionName = functionName,
242+
InvocationType = InvocationType.RequestResponse,
243+
Payload = JsonSerializer.Serialize(new APIGatewayProxyRequest
244+
{
245+
Body = "{\"user_id\":\"***\",\"product_id\":\"123456789\"}",
246+
RequestContext = new APIGatewayProxyRequest.ProxyRequestContext
247+
{
248+
AccountId = "123456789012",
249+
RequestId = requestId
250+
}
251+
}),
252+
LogType = LogType.Tail
253+
};
254+
255+
return await ExecuteRequest(request);
256+
}
257+
258+
private async Task<(APIGatewayProxyResponse Response, string Guid)> ExecuteHandlerRequest(
259+
string functionName, string payload)
260+
{
261+
var request = new InvokeRequest
262+
{
263+
FunctionName = functionName,
264+
InvocationType = InvocationType.RequestResponse,
265+
Payload = payload,
266+
LogType = LogType.Tail
267+
};
268+
269+
return await ExecuteRequest(request);
270+
}
271+
272+
private async Task<(APIGatewayProxyResponse Response, string Guid)> ExecuteRequest(InvokeRequest request)
273+
{
274+
var response = await _lambdaClient.InvokeAsync(request);
275+
276+
if (string.IsNullOrEmpty(response.LogResult))
277+
Assert.Fail("No LogResult field returned in the response of Lambda invocation.");
278+
279+
var responsePayload = System.Text.Encoding.UTF8.GetString(response.Payload.ToArray());
280+
var parsedResponse = JsonSerializer.Deserialize<APIGatewayProxyResponse>(responsePayload)
281+
?? throw new Exception("Failed to parse payload.");
282+
283+
string guid;
284+
try
285+
{
286+
// The GUID is inside the Response object
287+
var parsedBody = JsonSerializer.Deserialize<Response>(parsedResponse.Body);
288+
guid = parsedBody?.Guid ?? parsedResponse.Body;
289+
}
290+
catch (JsonException)
291+
{
292+
// For scenarios where the Body is already the GUID
293+
guid = parsedResponse.Body;
294+
}
295+
296+
return (parsedResponse, guid);
297+
}
337298
}
338299

339300
public record Response(string Greeting, string Guid);

0 commit comments

Comments
 (0)