@@ -330,6 +330,73 @@ class DataStoreEndToEndTests: SyncEngineIntegrationTestBase {
330
330
try validateSavePost ( )
331
331
}
332
332
333
+ /// Perform concurrent saves and observe the data successfuly synced from cloud. Then delete the items afterwards
334
+ /// and ensure they have successfully synced from cloud
335
+ ///
336
+ /// - Given: DataStore is in ready state
337
+ /// - When:
338
+ /// - Concurrently perform Save's
339
+ /// - Then:
340
+ /// - Ensure the expected mutation event with version 1 (synced from cloud) is received
341
+ /// - Clean up: Concurrently perform Delete's
342
+ /// - Ensure the expected mutation event with version 2 (synced from cloud) is received
343
+ ///
344
+ func testConcurrentSave( ) throws {
345
+ try startAmplifyAndWaitForReady ( )
346
+
347
+ var posts = [ Post] ( )
348
+ let count = 5
349
+ for _ in 0 ..< count {
350
+ let post = Post ( title: " title " ,
351
+ content: " content " ,
352
+ createdAt: . now( ) )
353
+ posts. append ( post)
354
+ }
355
+ let postsSyncedToCloud = expectation ( description: " All posts saved and synced to cloud " )
356
+ var postsSyncedToCloudCount = 0
357
+ let postsDeletedFromCloud = expectation ( description: " All posts deleted and synced to cloud " )
358
+ var postsDeletedFromCloudCount = 0
359
+ let sink = Amplify . DataStore. publisher ( for: Post . self) . sink { completed in
360
+ switch completed {
361
+ case . finished:
362
+ break
363
+ case . failure( let error) :
364
+ XCTFail ( " \( error) " )
365
+ }
366
+ } receiveValue: { mutationEvent in
367
+ if mutationEvent. mutationType == MutationEvent . MutationType. create. rawValue,
368
+ posts. contains ( where: { $0. id == mutationEvent. modelId } ) ,
369
+ mutationEvent. version == 1 {
370
+ postsSyncedToCloudCount += 1
371
+ self . log. debug ( " Post saved and synced from cloud \( mutationEvent. modelId) \( postsSyncedToCloudCount) " )
372
+ if postsSyncedToCloudCount == count {
373
+ postsSyncedToCloud. fulfill ( )
374
+ }
375
+ } else if mutationEvent. mutationType == MutationEvent . MutationType. delete. rawValue,
376
+ posts. contains ( where: { $0. id == mutationEvent. modelId } ) ,
377
+ mutationEvent. version == 2 {
378
+ postsDeletedFromCloudCount += 1
379
+ self . log. debug (
380
+ " Post deleted and synced from cloud \( mutationEvent. modelId) \( postsDeletedFromCloudCount) " )
381
+ if postsDeletedFromCloudCount == count {
382
+ postsDeletedFromCloud. fulfill ( )
383
+ }
384
+ }
385
+ }
386
+
387
+ DispatchQueue . concurrentPerform ( iterations: count) { index in
388
+ _ = Amplify . DataStore. save ( posts [ index] )
389
+ }
390
+
391
+ wait ( for: [ postsSyncedToCloud] , timeout: 100 )
392
+
393
+ DispatchQueue . concurrentPerform ( iterations: count) { index in
394
+ _ = Amplify . DataStore. delete ( posts [ index] )
395
+ }
396
+ wait ( for: [ postsDeletedFromCloud] , timeout: 100 )
397
+ sink. cancel ( )
398
+ }
399
+
333
400
// MARK: - Helpers
334
401
335
402
func validateSavePost( ) throws {
@@ -371,3 +438,8 @@ class DataStoreEndToEndTests: SyncEngineIntegrationTestBase {
371
438
wait ( for: [ createReceived] , timeout: networkTimeout)
372
439
}
373
440
}
441
+
442
+ @available ( iOS 13 . 0 , * )
443
+ extension DataStoreEndToEndTests : DefaultLogger {
444
+
445
+ }
0 commit comments