@@ -34,225 +34,105 @@ public FunctionTests(ITestOutputHelper testOutputHelper)
34
34
// await TestIdempotencyHandler(functionName);
35
35
// }
36
36
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
-
53
37
[ Theory ]
54
38
[ MemberData ( nameof ( TestData . Inline ) , MemberType = typeof ( TestData ) ) ]
55
39
public async Task IdempotencyPayloadSubsetTest ( string functionName , string tableName )
56
40
{
57
41
_tableName = tableName ;
58
- await TestIdempotencyPayloadSubset ( functionName ) ;
59
- }
60
-
61
- private async Task TestIdempotencyPayloadSubset ( string functionName )
62
- {
63
42
await UpdateFunctionHandler ( functionName , "Function::IdempotencyPayloadSubsetTest.Function::FunctionHandler" ) ;
64
43
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 ) ;
132
70
}
133
71
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 )
135
75
{
76
+ _tableName = tableName ;
136
77
await UpdateFunctionHandler ( functionName , "Function::IdempotencyAttributeTest.Function::FunctionHandler" ) ;
137
78
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 ) ;
202
107
}
203
108
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 )
205
112
{
113
+ _tableName = tableName ;
206
114
await UpdateFunctionHandler ( functionName , "Function::Function.Function::FunctionHandler" ) ;
207
115
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 ) ;
256
136
}
257
137
258
138
private async Task UpdateFunctionHandler ( string functionName , string handler )
@@ -334,6 +214,87 @@ private async Task AssertDynamoDbData(string id, string requestId, bool isSavedD
334
214
}
335
215
}
336
216
}
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
+ }
337
298
}
338
299
339
300
public record Response ( string Greeting , string Guid ) ;
0 commit comments