1414 */
1515
1616using System ;
17+ using System . Linq ;
1718using System . Text . Json ;
1819using System . Threading . Tasks ;
1920using Amazon . Lambda . TestUtilities ;
2324using AWS . Lambda . Powertools . Idempotency . Tests . Handlers ;
2425using AWS . Lambda . Powertools . Idempotency . Tests . Model ;
2526using FluentAssertions ;
26- using Moq ;
27+ using NSubstitute ;
28+ using NSubstitute . ExceptionExtensions ;
2729using Xunit ;
2830
2931[ assembly: CollectionBehavior ( DisableTestParallelization = true ) ]
@@ -39,10 +41,10 @@ public class IdempotentAspectTests : IDisposable
3941 public async Task Handle_WhenFirstCall_ShouldPutInStore ( Type type )
4042 {
4143 //Arrange
42- var store = new Mock < BasePersistenceStore > ( ) ;
44+ var store = Substitute . For < BasePersistenceStore > ( ) ;
4345 Idempotency . Configure ( builder =>
4446 builder
45- . WithPersistenceStore ( store . Object )
47+ . WithPersistenceStore ( store )
4648 . WithOptions ( optionsBuilder => optionsBuilder . WithEventKeyJmesPath ( "Id" ) )
4749 ) ;
4850
@@ -55,12 +57,18 @@ public async Task Handle_WhenFirstCall_ShouldPutInStore(Type type)
5557 //Assert
5658 basket . Products . Count . Should ( ) . Be ( 1 ) ;
5759 function . HandlerExecuted . Should ( ) . BeTrue ( ) ;
60+
61+ await store . Received ( ) . SaveInProgress (
62+ Arg . Is < JsonDocument > ( t =>
63+ t . ToString ( ) == JsonSerializer . SerializeToDocument ( product , new JsonSerializerOptions ( ) ) . ToString ( ) ) ,
64+ Arg . Any < DateTimeOffset > ( )
65+ ) ;
5866
59- store
60- . Verify ( x => x . SaveInProgress ( It . Is < JsonDocument > ( t => t . ToString ( ) == JsonSerializer . SerializeToDocument ( product , It . IsAny < JsonSerializerOptions > ( ) ) . ToString ( ) ) , It . IsAny < DateTimeOffset > ( ) ) ) ;
61-
62- store
63- . Verify ( x => x . SaveSuccess ( It . IsAny < JsonDocument > ( ) , It . Is < Basket > ( y => y . Equals ( basket ) ) , It . IsAny < DateTimeOffset > ( ) ) ) ;
67+ await store . Received ( ) . SaveSuccess (
68+ Arg . Any < JsonDocument > ( ) ,
69+ Arg . Is < Basket > ( y => y . Equals ( basket ) ) ,
70+ Arg . Any < DateTimeOffset > ( )
71+ ) ;
6472 }
6573
6674 [ Theory ]
@@ -69,14 +77,14 @@ public async Task Handle_WhenFirstCall_ShouldPutInStore(Type type)
6977 public async Task Handle_WhenSecondCall_AndNotExpired_ShouldGetFromStore ( Type type )
7078 {
7179 //Arrange
72- var store = new Mock < BasePersistenceStore > ( ) ;
73- store . Setup ( x => x . SaveInProgress ( It . IsAny < JsonDocument > ( ) , It . IsAny < DateTimeOffset > ( ) ) )
74- . Throws < IdempotencyItemAlreadyExistsException > ( ) ;
80+ var store = Substitute . For < BasePersistenceStore > ( ) ;
81+ store . SaveInProgress ( Arg . Any < JsonDocument > ( ) , Arg . Any < DateTimeOffset > ( ) )
82+ . Returns ( _ => throw new IdempotencyItemAlreadyExistsException ( ) ) ;
7583
7684 // GIVEN
7785 Idempotency . Configure ( builder =>
7886 builder
79- . WithPersistenceStore ( store . Object )
87+ . WithPersistenceStore ( store )
8088 . WithOptions ( optionsBuilder => optionsBuilder . WithEventKeyJmesPath ( "Id" ) )
8189 ) ;
8290
@@ -88,9 +96,8 @@ public async Task Handle_WhenSecondCall_AndNotExpired_ShouldGetFromStore(Type ty
8896 DateTimeOffset . UtcNow . AddSeconds ( 356 ) . ToUnixTimeSeconds ( ) ,
8997 JsonSerializer . SerializeToNode ( basket ) ! . ToString ( ) ,
9098 null ) ;
91- store . Setup ( x=> x . GetRecord ( It . IsAny < JsonDocument > ( ) , It . IsAny < DateTimeOffset > ( ) ) )
92- . ReturnsAsync ( record ) ;
93-
99+ store . GetRecord ( Arg . Any < JsonDocument > ( ) , Arg . Any < DateTimeOffset > ( ) ) . Returns ( record ) ;
100+
94101 var function = Activator . CreateInstance ( type ) as IIdempotencyEnabledFunction ;
95102
96103 // Act
@@ -107,15 +114,16 @@ public async Task Handle_WhenSecondCall_AndNotExpired_ShouldGetFromStore(Type ty
107114 public async Task Handle_WhenSecondCall_AndStatusInProgress_ShouldThrowIdempotencyAlreadyInProgressException ( Type type )
108115 {
109116 // Arrange
110- var store = new Mock < BasePersistenceStore > ( ) ;
111-
117+ var store = Substitute . For < BasePersistenceStore > ( ) ;
118+
112119 Idempotency . Configure ( builder =>
113120 builder
114- . WithPersistenceStore ( store . Object )
121+ . WithPersistenceStore ( store )
115122 . WithOptions ( optionsBuilder => optionsBuilder . WithEventKeyJmesPath ( "Id" ) )
116123 ) ;
117- store . Setup ( x=> x . SaveInProgress ( It . IsAny < JsonDocument > ( ) , It . IsAny < DateTimeOffset > ( ) ) )
118- . Throws < IdempotencyItemAlreadyExistsException > ( ) ;
124+
125+ store . SaveInProgress ( Arg . Any < JsonDocument > ( ) , Arg . Any < DateTimeOffset > ( ) )
126+ . Returns ( _ => throw new IdempotencyItemAlreadyExistsException ( ) ) ;
119127
120128 var product = new Product ( 42 , "fake product" , 12 ) ;
121129 var basket = new Basket ( product ) ;
@@ -125,9 +133,9 @@ public async Task Handle_WhenSecondCall_AndStatusInProgress_ShouldThrowIdempoten
125133 DateTimeOffset . UtcNow . AddSeconds ( 356 ) . ToUnixTimeSeconds ( ) ,
126134 JsonSerializer . SerializeToNode ( basket ) ! . ToString ( ) ,
127135 null ) ;
128- store . Setup ( x => x . GetRecord ( It . IsAny < JsonDocument > ( ) , It . IsAny < DateTimeOffset > ( ) ) )
129- . ReturnsAsync ( record ) ;
130-
136+ store . GetRecord ( Arg . Any < JsonDocument > ( ) , Arg . Any < DateTimeOffset > ( ) )
137+ . Returns ( record ) ;
138+
131139 // Act
132140 var function = Activator . CreateInstance ( type ) as IIdempotencyEnabledFunction ;
133141 Func < Task > act = async ( ) => await function ! . HandleTest ( product , new TestLambdaContext ( ) ) ;
@@ -142,11 +150,11 @@ public async Task Handle_WhenSecondCall_AndStatusInProgress_ShouldThrowIdempoten
142150 public async Task Handle_WhenThrowException_ShouldDeleteRecord_AndThrowFunctionException ( Type type )
143151 {
144152 // Arrange
145- var store = new Mock < BasePersistenceStore > ( ) ;
153+ var store = Substitute . For < BasePersistenceStore > ( ) ;
146154
147155 Idempotency . Configure ( builder =>
148156 builder
149- . WithPersistenceStore ( store . Object )
157+ . WithPersistenceStore ( store )
150158 . WithOptions ( optionsBuilder => optionsBuilder . WithEventKeyJmesPath ( "Id" ) )
151159 ) ;
152160
@@ -158,24 +166,22 @@ public async Task Handle_WhenThrowException_ShouldDeleteRecord_AndThrowFunctionE
158166
159167 // Assert
160168 await act . Should ( ) . ThrowAsync < IndexOutOfRangeException > ( ) ;
161- store . Verify (
162- x => x . DeleteRecord ( It . IsAny < JsonDocument > ( ) , It . IsAny < IndexOutOfRangeException > ( ) ) ) ;
169+ await store . Received ( ) . DeleteRecord ( Arg . Any < JsonDocument > ( ) , Arg . Any < IndexOutOfRangeException > ( ) ) ;
163170 }
164171
165172 [ Theory ]
166173 [ InlineData ( typeof ( IdempotencyEnabledFunction ) ) ]
167174 [ InlineData ( typeof ( IdempotencyEnabledSyncFunction ) ) ]
168175 public async Task Handle_WhenIdempotencyDisabled_ShouldJustRunTheFunction ( Type type )
169176 {
170-
171177 // Arrange
172- var store = new Mock < BasePersistenceStore > ( ) ;
178+ var store = Substitute . For < BasePersistenceStore > ( ) ;
173179
174180 Environment . SetEnvironmentVariable ( Constants . IdempotencyDisabledEnv , "true" ) ;
175181
176182 Idempotency . Configure ( builder =>
177183 builder
178- . WithPersistenceStore ( store . Object )
184+ . WithPersistenceStore ( store )
179185 . WithOptions ( optionsBuilder => optionsBuilder . WithEventKeyJmesPath ( "Id" ) )
180186 ) ;
181187
@@ -186,7 +192,7 @@ public async Task Handle_WhenIdempotencyDisabled_ShouldJustRunTheFunction(Type t
186192 var basket = await function ! . HandleTest ( product , new TestLambdaContext ( ) ) ;
187193
188194 // Assert
189- store . Invocations . Count . Should ( ) . Be ( 0 ) ;
195+ store . ReceivedCalls ( ) . Count ( ) . Should ( ) . Be ( 0 ) ;
190196 basket . Products . Count . Should ( ) . Be ( 1 ) ;
191197 function . HandlerExecuted . Should ( ) . BeTrue ( ) ;
192198 }
@@ -198,72 +204,76 @@ public void Idempotency_Set_Execution_Environment_Context()
198204 var assemblyName = "AWS.Lambda.Powertools.Idempotency" ;
199205 var assemblyVersion = "1.0.0" ;
200206
201- var env = new Mock < IPowertoolsEnvironment > ( ) ;
202- env . Setup ( x => x . GetAssemblyName ( It . IsAny < Idempotency > ( ) ) ) . Returns ( assemblyName ) ;
203- env . Setup ( x => x . GetAssemblyVersion ( It . IsAny < Idempotency > ( ) ) ) . Returns ( assemblyVersion ) ;
207+ var env = Substitute . For < IPowertoolsEnvironment > ( ) ;
208+ env . GetAssemblyName ( Arg . Any < Idempotency > ( ) ) . Returns ( assemblyName ) ;
209+ env . GetAssemblyVersion ( Arg . Any < Idempotency > ( ) ) . Returns ( assemblyVersion ) ;
204210
205- var conf = new PowertoolsConfigurations ( new SystemWrapper ( env . Object ) ) ;
211+ var conf = new PowertoolsConfigurations ( new SystemWrapper ( env ) ) ;
206212
207213 // Act
208214 var xRayRecorder = new Idempotency ( conf ) ;
209215
210216 // Assert
211- env . Verify ( v =>
212- v . SetEnvironmentVariable (
213- "AWS_EXECUTION_ENV" , $ "{ Constants . FeatureContextIdentifier } /Idempotency/{ assemblyVersion } "
214- ) , Times . Once ) ;
215-
216- env . Verify ( v =>
217- v . GetEnvironmentVariable (
218- "AWS_EXECUTION_ENV"
219- ) , Times . Once ) ;
217+ env . Received ( 1 ) . SetEnvironmentVariable (
218+ "AWS_EXECUTION_ENV" ,
219+ $ "{ Constants . FeatureContextIdentifier } /Idempotency/{ assemblyVersion } "
220+ ) ;
221+
222+ env . Received ( 1 ) . GetEnvironmentVariable ( "AWS_EXECUTION_ENV" ) ;
220223
221224 Assert . NotNull ( xRayRecorder ) ;
222225 }
223-
226+
224227 [ Fact ]
225- public void Handle_WhenIdempotencyOnSubMethodAnnotated_AndFirstCall_ShouldPutInStore ( )
228+ public async Task Handle_WhenIdempotencyOnSubMethodAnnotated_AndFirstCall_ShouldPutInStore ( )
226229 {
227230 // Arrange
228- var store = new Mock < BasePersistenceStore > ( ) ;
229- Idempotency . Configure ( builder => builder . WithPersistenceStore ( store . Object ) ) ;
231+ var store = Substitute . For < BasePersistenceStore > ( ) ;
232+ Idempotency . Configure ( builder => builder . WithPersistenceStore ( store ) ) ;
230233
231234 // Act
232235 IdempotencyInternalFunction function = new IdempotencyInternalFunction ( ) ;
233236 Product product = new Product ( 42 , "fake product" , 12 ) ;
234237 Basket resultBasket = function . HandleRequest ( product , new TestLambdaContext ( ) ) ;
235-
238+
236239 // Assert
237240 resultBasket . Products . Count . Should ( ) . Be ( 2 ) ;
238241 function . IsSubMethodCalled . Should ( ) . BeTrue ( ) ;
239-
240- store
241- . Verify ( x=> x . SaveInProgress ( It . Is < JsonDocument > ( t=> t . ToString ( ) == JsonSerializer . SerializeToDocument ( "fake" , It . IsAny < JsonSerializerOptions > ( ) ) . ToString ( ) ) , It . IsAny < DateTimeOffset > ( ) ) ) ;
242242
243- store
244- . Verify ( x=> x . SaveSuccess ( It . IsAny < JsonDocument > ( ) , It . Is < Basket > ( y => y . Equals ( resultBasket ) ) , It . IsAny < DateTimeOffset > ( ) ) ) ;
243+ await store
244+ . Received ( 1 )
245+ . SaveInProgress (
246+ Arg . Is < JsonDocument > ( t =>
247+ t . ToString ( ) == JsonSerializer . SerializeToDocument ( "fake" , new JsonSerializerOptions ( ) )
248+ . ToString ( ) ) ,
249+ Arg . Any < DateTimeOffset > ( ) ) ;
250+
251+ await store
252+ . Received ( 1 )
253+ . SaveSuccess ( Arg . Any < JsonDocument > ( ) , Arg . Is < Basket > ( y => y . Equals ( resultBasket ) ) ,
254+ Arg . Any < DateTimeOffset > ( ) ) ;
245255 }
246256
247257 [ Fact ]
248258 public void Handle_WhenIdempotencyOnSubMethodAnnotated_AndSecondCall_AndNotExpired_ShouldGetFromStore ( )
249259 {
250260 // Arrange
251- var store = new Mock < BasePersistenceStore > ( ) ;
252- store . Setup ( x => x . SaveInProgress ( It . IsAny < JsonDocument > ( ) , It . IsAny < DateTimeOffset > ( ) ) )
253- . Throws < IdempotencyItemAlreadyExistsException > ( ) ;
261+ var store = Substitute . For < BasePersistenceStore > ( ) ;
262+ store . SaveInProgress ( Arg . Any < JsonDocument > ( ) , Arg . Any < DateTimeOffset > ( ) )
263+ . Throws ( new IdempotencyItemAlreadyExistsException ( ) ) ;
254264
255- Idempotency . Configure ( builder => builder . WithPersistenceStore ( store . Object ) ) ;
265+ Idempotency . Configure ( builder => builder . WithPersistenceStore ( store ) ) ;
256266
257- Product product = new Product ( 42 , "fake product" , 12 ) ;
258- Basket basket = new Basket ( product ) ;
259- DataRecord record = new DataRecord (
267+ var product = new Product ( 42 , "fake product" , 12 ) ;
268+ var basket = new Basket ( product ) ;
269+ var record = new DataRecord (
260270 "fake" ,
261271 DataRecord . DataRecordStatus . COMPLETED ,
262272 DateTimeOffset . UtcNow . AddSeconds ( 356 ) . ToUnixTimeSeconds ( ) ,
263273 JsonSerializer . SerializeToNode ( basket ) ! . ToString ( ) ,
264274 null ) ;
265- store . Setup ( x => x . GetRecord ( It . IsAny < JsonDocument > ( ) , It . IsAny < DateTimeOffset > ( ) ) )
266- . ReturnsAsync ( record ) ;
275+ store . GetRecord ( Arg . Any < JsonDocument > ( ) , Arg . Any < DateTimeOffset > ( ) )
276+ . Returns ( record ) ;
267277
268278 // Act
269279 var function = new IdempotencyInternalFunction ( ) ;
@@ -298,10 +308,10 @@ public void Handle_WhenIdempotencyOnSubMethodAnnotated_AndKeyJMESPath_ShouldPutI
298308 public void Handle_WhenIdempotencyOnSubMethodNotAnnotated_ShouldThrowException ( )
299309 {
300310 // Arrange
301- var store = new Mock < BasePersistenceStore > ( ) ;
311+ var store = Substitute . For < BasePersistenceStore > ( ) ;
302312 Idempotency . Configure ( builder =>
303313 builder
304- . WithPersistenceStore ( store . Object )
314+ . WithPersistenceStore ( store )
305315 ) ;
306316
307317 // Act
@@ -317,10 +327,10 @@ public void Handle_WhenIdempotencyOnSubMethodNotAnnotated_ShouldThrowException()
317327 public void Handle_WhenIdempotencyOnSubMethodVoid_ShouldThrowException ( )
318328 {
319329 // Arrange
320- var store = new Mock < BasePersistenceStore > ( ) ;
330+ var store = Substitute . For < BasePersistenceStore > ( ) ;
321331 Idempotency . Configure ( builder =>
322332 builder
323- . WithPersistenceStore ( store . Object )
333+ . WithPersistenceStore ( store )
324334 ) ;
325335
326336 // Act
0 commit comments