private async Task TestSyncAgentDeleteWithForeignKeys( BlogDbContext localDb, ISyncProvider localSyncProvider, BlogDbContext remoteDb, ISyncProvider remoteSyncProvider) { var syncAgent = new SyncAgent(localSyncProvider, remoteSyncProvider); await syncAgent.SynchronizeAsync(); //create a user on remote store User remoteUser; remoteDb.Users.Add(remoteUser = new User() { Email = "*****@*****.**", Name = "User", Created = DateTime.Now }); remoteUser.Posts.Add(new Post() { Content = $"Content", Title = $"Post", Claps = 1, Stars = 10 }); await remoteDb.SaveChangesAsync(); await syncAgent.SynchronizeAsync(); var localUser = await localDb.Users.FirstOrDefaultAsync(_ => _.Email == "*****@*****.**"); localUser.ShouldNotBeNull(); //delete user on remotedb remoteDb = remoteDb.Refresh(); remoteDb.Users.Remove(await remoteDb.Users.FirstAsync(_ => _.Email == "*****@*****.**")); remoteDb.Posts.RemoveRange(await remoteDb.Posts.ToListAsync()); await remoteDb.SaveChangesAsync(); await syncAgent.SynchronizeAsync(); localDb = localDb.Refresh(); localUser = await localDb.Users.FirstOrDefaultAsync(_ => _.Email == "*****@*****.**"); localUser.ShouldBeNull(); (await localDb.Posts.AnyAsync()).ShouldBeFalse(); }
private async Task TestSyncAgent( BlogDbContext localDb, ISyncProvider localSyncProvider, BlogDbContext remoteDb, ISyncProvider remoteSyncProvider) { var syncAgent = new SyncAgent(localSyncProvider, remoteSyncProvider); await syncAgent.SynchronizeAsync(); //create a user on server var remoteUser = new User() { Email = "*****@*****.**", Name = "user", Created = new DateTime(2018, 1, 1) }; remoteDb.Users.Add(remoteUser); await remoteDb.SaveChangesAsync(); //sync with remote server await syncAgent.SynchronizeAsync(); remoteDb = remoteDb.Refresh(); //discard any cache data in ef //verify that new user is stored now locally too var localUser = await localDb.Users.FirstAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual("user", localUser.Name); Assert.AreEqual(new DateTime(2018, 1, 1), localUser.Created); //create an article for user locally var localPost = new Post() { Content = "this is my first post", Title = "First Post", Updated = DateTime.Now.Date }; localUser.Posts.Add(localPost); await localDb.SaveChangesAsync(); //sync with remote server await syncAgent.SynchronizeAsync(); remoteDb = remoteDb.Refresh(); //discard any cache data in ef //verify that user on server and locally have the same post remoteUser = await remoteDb.Users.Include(_ => _.Posts).FirstOrDefaultAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual("user", remoteUser.Name); Assert.AreEqual(new DateTime(2018, 1, 1), remoteUser.Created); Assert.AreEqual(1, remoteUser.Posts.Count); var remotePost = remoteUser.Posts[0]; Assert.AreEqual(localPost.Author.Name, remotePost.Author.Name); Assert.AreEqual(localPost.Content, remotePost.Content); Assert.AreEqual(localPost.Title, remotePost.Title); //now make a change to post content while claps it on server localPost.Content = "this is my my first post edited"; await localDb.SaveChangesAsync(); remotePost.Claps += 1; await remoteDb.SaveChangesAsync(); //then sync await syncAgent.SynchronizeAsync(conflictResolutionOnLocalStore : ConflictResolution.ForceWrite); remoteDb = remoteDb.Refresh(); //discard any cache data in ef //verify that claps is 1 on both server and local stores //content is not changed because we set conflictResolutionOnLocalStore to ConflictResolution.ForceWrite //so server skipped our try to update content while local store forcely write data coming from server remoteUser = await remoteDb.Users.Include(_ => _.Posts).FirstAsync(_ => _.Email == "*****@*****.**"); remotePost = remoteUser.Posts[0]; Assert.AreEqual("user", remotePost.Author.Name); Assert.AreEqual("this is my first post", remotePost.Content); Assert.AreEqual(1, remotePost.Claps); localUser = await localDb.Users.FirstAsync(_ => _.Email == "*****@*****.**"); localPost = localUser.Posts[0]; Assert.AreEqual("user", remotePost.Author.Name); Assert.AreEqual("this is my first post", remotePost.Content); Assert.AreEqual(1, remotePost.Claps); //so to handle this scenario (when a record is often edited on multiple devices) //we should take care of restoring any pending records (posts) locally //for example using a PendingPosts table (not synched) }
private async Task TestSyncAgentWithDataRetention( BlogDbContext localDb, ISyncProvider localSyncProvider, BlogDbContext remoteDb, ISyncProvider remoteSyncProvider) { //setup change tracking await remoteSyncProvider.ApplyProvisionAsync(); //create remote data User remoteUser; remoteDb.Users.Add(remoteUser = new User() { Email = "*****@*****.**", Name = "User created before sync", Created = DateTime.Now }); for (int i = 1; i <= 10; i++) { remoteUser.Posts.Add(new Post() { Content = $"Content of Post {i}", Title = $"Post {i}", Claps = 1, Stars = 10 }); } await remoteDb.SaveChangesAsync(); //set data retention -> remove first records in the change tracking table var remoteSyncVersion = await remoteSyncProvider.GetSyncVersionAsync(); remoteSyncVersion.Minimum.ShouldBe(1); remoteSyncVersion.Current.ShouldBe(11);//1 users + 10 posts remoteSyncVersion = await remoteSyncProvider.ApplyRetentionPolicyAsync(4); remoteSyncVersion.Minimum.ShouldBe(4); remoteSyncVersion.Current.ShouldBe(11);// 11 - 4 = 7 posts //local provider is still not synched var localSyncVersion = await localSyncProvider.GetSyncVersionAsync(); localSyncVersion.Minimum.ShouldBe(0); localSyncVersion.Current.ShouldBe(0); //now let's sync var syncAgent = new SyncAgent(localSyncProvider, remoteSyncProvider); await syncAgent.SynchronizeAsync(); localSyncVersion = await localSyncProvider.GetSyncVersionAsync(); localSyncVersion.Minimum.ShouldBe(0); localSyncVersion.Current.ShouldBe(0); (await localDb.Users.ToListAsync()).Count.ShouldBe(1); (await localDb.Posts.ToListAsync()).Count.ShouldBe(10); //now add a new post on server, one on local db than sync remoteDb = remoteDb.Refresh(); remoteUser = await remoteDb.Users.FirstAsync(_ => _.Email == "*****@*****.**"); remoteUser.Posts.Add(new Post() { Content = $"Another post on server", Title = $"New post on server", Claps = 1, Stars = 10 }); await remoteDb.SaveChangesAsync(); var localUser = await localDb.Users.FirstAsync(_ => _.Email == "*****@*****.**"); localUser.Posts.Add(new Post() { Content = $"A post from client", Title = $"New post on client", Claps = 1, Stars = 10 }); await localDb.SaveChangesAsync(); await syncAgent.SynchronizeAsync(); remoteDb = remoteDb.Refresh(); localDb = localDb.Refresh(); (await remoteDb.Users.SingleAsync()).Email.ShouldBe("*****@*****.**"); (await localDb.Users.SingleAsync()).Email.ShouldBe("*****@*****.**"); (await remoteDb.Posts.ToListAsync()).Count.ShouldBe(12); (await localDb.Posts.ToListAsync()).Count.ShouldBe(12); remoteSyncVersion = await remoteSyncProvider.ApplyRetentionPolicyAsync(4); remoteSyncVersion.Minimum.ShouldBe(4); remoteSyncVersion.Current.ShouldBe(13);// +2 posts localSyncVersion = await localSyncProvider.GetSyncVersionAsync(); localSyncVersion.Minimum.ShouldBe(1); localSyncVersion.Current.ShouldBe(2); // +2 posts }
private async Task TestSyncAgentWithUpdatedRemoteDeletedLocal( BlogDbContext localDb, ISyncProvider localSyncProvider, BlogDbContext remoteDb, ISyncProvider remoteSyncProvider) { #region Initialize remote data, sync local and verify User remoteUser; remoteDb.Users.Add(remoteUser = new User() { Email = "*****@*****.**", Name = "User created before sync", Created = DateTime.Now }); remoteUser.Posts.Add(new Post() { Content = "This is a post created before sync of the client", Title = "Initial post of user 1", Claps = 1, Stars = 10 }); remoteUser.Posts.Add(new Post() { Content = "This is a second post created before sync of the client", Title = "Initial post 2 of user 1", Claps = 2, Stars = 1 }); remoteUser.Posts.Add(new Post() { Content = "This is a third post created before sync of the client", Title = "Initial post 3 of user 1", Claps = 3, Stars = 1 }); await remoteDb.SaveChangesAsync(); var syncAgent = new SyncAgent(localSyncProvider, remoteSyncProvider); await syncAgent.SynchronizeAsync(); var localUser = await localDb.Users.Include(_ => _.Posts).FirstOrDefaultAsync(_ => _.Email == "*****@*****.**"); localUser.ShouldNotBeNull(); localUser.Email.ShouldBe("*****@*****.**"); localUser.Name.ShouldBe("User created before sync"); var localUserPosts = localUser.Posts.OrderBy(_ => _.Claps).ToList(); localUserPosts.Count().ShouldBe(3); localUserPosts[0].Content.ShouldBe("This is a post created before sync of the client"); localUserPosts[0].Title.ShouldBe("Initial post of user 1"); localUserPosts[0].Claps.ShouldBe(1); localUserPosts[0].Stars.ShouldBe(10); localUserPosts[1].Content.ShouldBe("This is a second post created before sync of the client"); localUserPosts[1].Title.ShouldBe("Initial post 2 of user 1"); localUserPosts[1].Claps.ShouldBe(2); localUserPosts[1].Stars.ShouldBe(1); localUserPosts[2].Content.ShouldBe("This is a third post created before sync of the client"); localUserPosts[2].Title.ShouldBe("Initial post 3 of user 1"); localUserPosts[2].Claps.ShouldBe(3); localUserPosts[2].Stars.ShouldBe(1); await syncAgent.SynchronizeAsync(); remoteDb = remoteDb.Refresh(); var remoteUserPosts = remoteDb.Posts.OrderBy(_ => _.Claps).ToList(); remoteUserPosts.Count().ShouldBe(3); remoteUserPosts[0].Content.ShouldBe("This is a post created before sync of the client"); //even if edited on localdb post that was synched as initial snapshot can't be modified on server remoteUserPosts[0].Title.ShouldBe("Initial post of user 1"); remoteUserPosts[0].Claps.ShouldBe(1); remoteUserPosts[0].Stars.ShouldBe(10); remoteUserPosts[1].Content.ShouldBe("This is a second post created before sync of the client"); remoteUserPosts[1].Title.ShouldBe("Initial post 2 of user 1"); remoteUserPosts[1].Claps.ShouldBe(2); remoteUserPosts[1].Stars.ShouldBe(1); remoteUserPosts[2].Content.ShouldBe("This is a third post created before sync of the client"); remoteUserPosts[2].Title.ShouldBe("Initial post 3 of user 1"); remoteUserPosts[2].Claps.ShouldBe(3); remoteUserPosts[2].Stars.ShouldBe(1); #endregion #region Remove a local record and verify var postToRemove = localUser.Posts[2]; localDb.Posts.Remove(localUser.Posts[2]); await localDb.SaveChangesAsync(); localUser = await localDb.Users.Include(_ => _.Posts).FirstOrDefaultAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual(2, localUser.Posts.Count); #endregion #region Update a remote record var remotePost = remoteUser.Posts.First(p => p.Id == postToRemove.Id); remotePost.Content = "Updated remote for test."; remoteDb = remoteDb.Refresh(); remoteDb.Posts.Update(remotePost); await remoteDb.SaveChangesAsync(); remoteUser = await remoteDb.Users.Include(_ => _.Posts).FirstOrDefaultAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual("Updated remote for test.", remoteUser.Posts.First(p => p.Id == postToRemove.Id).Content); #endregion #region Syncronize and verify the local database has the deleted record //I've delete a post on local database AND updated the same posts on rempte database //SynchronizeAsync() accepts 2 args remoteConflictResolutionFunc and localConflictResolutionFunc //that describe how agent should deal with conflits: //1) remoteConflictResolutionFunc: (itemInConflict) => ConflictResolution.Skip or simply remoteConflictResolution: ConflictResolution.Skip // means that remote database should not apply the delete operation on post coming from local database //2) localConflictResolutionFunc: (itemInConflict) => ConflictResolution.ForceWrite or simply localConflictResolutionFunc: ConflictResolution.ForceWrite // means that local datase should update the local post that was updated from remote db and since the record doesn't exist anymore it actually means insert // the post record again with data coming from server await syncAgent.SynchronizeAsync(); //this above call is the same as this: // await syncAgent.SynchronizeAsync(conflictResolutionOnRemoteStore: ConflictResolution.Skip, conflictResolutionOnLocalStore: ConflictResolution.ForceWrite); //Thanks to Mike Perrenoud I've found that by default await syncAgent.SynchronizeAsync() was instead defaulted to // await syncAgent.SynchronizeAsync(conflictResolutionOnRemoteStore: ConflictResolution.Skip, conflictResolutionOnLocalStore: ConflictResolution.Skip); // causing this test to fail // following code allows to enumerate any item in conflict and take appropriate action for each of them: //await syncAgent.SynchronizeAsync( // remoteConflictResolutionFunc: (itemInConflict) => // { // itemInConflict.ChangeType.ShouldBe(ChangeType.Delete); // itemInConflict.TableName.ShouldBe("Posts"); // itemInConflict.Values["Id"].Value = postToRemove.Id; // return ConflictResolution.Skip; // }, // localConflictResolutionFunc: (itemInConflict) => // { // return ConflictResolution.ForceWrite; // }); localDb = localDb.Refresh(); localUser = await localDb.Users.Include(_ => _.Posts).FirstOrDefaultAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual(3, localUser.Posts.Count); Assert.AreEqual("Updated remote for test.", localUser.Posts.First(p => p.Id == postToRemove.Id).Content); #endregion }
private async Task TestSyncAgentWithInitialData( BlogDbContext localDb, ISyncProvider localSyncProvider, BlogDbContext remoteDb, ISyncProvider remoteSyncProvider) { User remoteUser; remoteDb.Users.Add(remoteUser = new User() { Email = "*****@*****.**", Name = "User created before sync", Created = DateTime.Now }); remoteUser.Posts.Add(new Post() { Content = "This is a post created before sync of the client", Title = "Initial post of user 1", Claps = 1, Stars = 10 }); remoteUser.Posts.Add(new Post() { Content = "This is a second post created before sync of the client", Title = "Initial post 2 of user 1", Claps = 2, Stars = 1 }); await remoteDb.SaveChangesAsync(); await remoteSyncProvider.ApplyProvisionAsync(); remoteUser.Posts.Add(new Post() { Content = "This is a third post created before sync of the client but after applying provision to remote db", Title = "Initial post 3 of user 1", Claps = 3, Stars = 1 }); await remoteDb.SaveChangesAsync(); var syncAgent = new SyncAgent(localSyncProvider, remoteSyncProvider); await syncAgent.SynchronizeAsync(); var localUser = await localDb.Users.Include(_ => _.Posts).FirstOrDefaultAsync(_ => _.Email == "*****@*****.**"); localUser.ShouldNotBeNull(); localUser.Email.ShouldBe("*****@*****.**"); localUser.Name.ShouldBe("User created before sync"); var localUserPosts = localUser.Posts.OrderBy(_ => _.Claps).ToList(); localUserPosts.Count().ShouldBe(3); localUserPosts[0].Content.ShouldBe("This is a post created before sync of the client"); localUserPosts[0].Title.ShouldBe("Initial post of user 1"); localUserPosts[0].Claps.ShouldBe(1); localUserPosts[0].Stars.ShouldBe(10); localUserPosts[1].Content.ShouldBe("This is a second post created before sync of the client"); localUserPosts[1].Title.ShouldBe("Initial post 2 of user 1"); localUserPosts[1].Claps.ShouldBe(2); localUserPosts[1].Stars.ShouldBe(1); localUserPosts[2].Content.ShouldBe("This is a third post created before sync of the client but after applying provision to remote db"); localUserPosts[2].Title.ShouldBe("Initial post 3 of user 1"); localUserPosts[2].Claps.ShouldBe(3); localUserPosts[2].Stars.ShouldBe(1); await syncAgent.SynchronizeAsync(); localUser.Posts.Add(new Post() { Content = "Post created on local db after first sync", Title = "Post created on local db", Claps = 4 }); localUserPosts[0].Title = "Post edited on local db"; await localDb.SaveChangesAsync(); await syncAgent.SynchronizeAsync(); remoteDb = remoteDb.Refresh(); var remoteUserPosts = remoteDb.Posts.OrderBy(_ => _.Claps).ToList(); remoteUserPosts.Count().ShouldBe(4); remoteUserPosts[0].Content.ShouldBe("This is a post created before sync of the client"); //even if edited on localdb post that was synched as initial snapshot can't be modified on server remoteUserPosts[0].Title.ShouldBe("Initial post of user 1"); remoteUserPosts[0].Claps.ShouldBe(1); remoteUserPosts[0].Stars.ShouldBe(10); remoteUserPosts[1].Content.ShouldBe("This is a second post created before sync of the client"); remoteUserPosts[1].Title.ShouldBe("Initial post 2 of user 1"); remoteUserPosts[1].Claps.ShouldBe(2); remoteUserPosts[1].Stars.ShouldBe(1); remoteUserPosts[2].Content.ShouldBe("This is a third post created before sync of the client but after applying provision to remote db"); remoteUserPosts[2].Title.ShouldBe("Initial post 3 of user 1"); remoteUserPosts[2].Claps.ShouldBe(3); remoteUserPosts[2].Stars.ShouldBe(1); remoteUserPosts[3].Content.ShouldBe("Post created on local db after first sync"); remoteUserPosts[3].Title.ShouldBe("Post created on local db"); remoteUserPosts[3].Claps.ShouldBe(4); remoteUserPosts[3].Stars.ShouldBe(0); }
private async Task TestSyncAgentMultipleRecordsOnSameTable( BlogDbContext localDb, ISyncProvider localSyncProvider, BlogDbContext remoteDb, ISyncProvider remoteSyncProvider) { var syncAgent = new SyncAgent(localSyncProvider, remoteSyncProvider); //await syncAgent.InitializeAsync(); await syncAgent.SynchronizeAsync(); //create a user on server var remoteUser = new User() { Email = "*****@*****.**", Name = "user", Created = new DateTime(2018, 1, 1) }; remoteDb.Users.Add(remoteUser); await remoteDb.SaveChangesAsync(); //create a second user on server var remoteUser2 = new User() { Email = "*****@*****.**", Name = "user2", Created = new DateTime(2019, 1, 1) }; remoteDb.Users.Add(remoteUser2); await remoteDb.SaveChangesAsync(); //sync with remote server await syncAgent.SynchronizeAsync(); //verify that new user is stored now locally too var localUser = await localDb.Users.FirstAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual("user", localUser.Name); Assert.AreEqual(new DateTime(2018, 1, 1), localUser.Created); //verify that new second user is stored now locally too var localUser2 = await localDb.Users.FirstAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual("user2", localUser2.Name); Assert.AreEqual(new DateTime(2019, 1, 1), localUser2.Created); localUser.Posts.Add(new Post() { Content = "This is first post from user 1", Updated = new DateTime(2019, 1, 1) }); localUser.Posts.Add(new Post() { Content = "This is second post from user 1", Updated = new DateTime(2019, 1, 2) }); localUser2.Posts.Add(new Post() { Content = "This is first post from user 2", Updated = DateTime.Now }); await localDb.SaveChangesAsync(); //create a third user on server var remoteUser3 = new User() { Email = "*****@*****.**", Name = "user3", Created = new DateTime(2019, 1, 1) }; remoteDb.Users.Add(remoteUser3); await remoteDb.SaveChangesAsync(); await syncAgent.SynchronizeAsync(); localDb = localDb.Refresh(); //verify that first user is still stored locally localUser = await localDb.Users.Include(_ => _.Posts).FirstAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual("user", localUser.Name); Assert.AreEqual(new DateTime(2018, 1, 1), localUser.Created); //verify that second user is still stored locally localUser2 = await localDb.Users.Include(_ => _.Posts).FirstAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual("user2", localUser2.Name); Assert.AreEqual(new DateTime(2019, 1, 1), localUser2.Created); //verify that new third user is stored locally now var localUser3 = await localDb.Users.Include(_ => _.Posts).FirstAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual("user3", localUser3.Name); Assert.AreEqual(new DateTime(2019, 1, 1), localUser3.Created); //verify that user posts are still stored locally Assert.AreEqual("This is first post from user 1", localUser.Posts.OrderBy(_ => _.Updated).ToArray()[0].Content); Assert.AreEqual("This is second post from user 1", localUser.Posts.OrderBy(_ => _.Updated).ToArray()[1].Content); Assert.AreEqual("This is first post from user 2", localUser2.Posts.OrderBy(_ => _.Updated).ToArray()[0].Content); remoteDb = remoteDb.Refresh(); //verify that first user is still stored remotely remoteUser = await remoteDb.Users.Include(_ => _.Posts).FirstAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual("user", remoteUser.Name); Assert.AreEqual(new DateTime(2018, 1, 1), remoteUser.Created); //verify that second user is still stored remotely remoteUser2 = await remoteDb.Users.Include(_ => _.Posts).FirstAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual("user2", remoteUser2.Name); Assert.AreEqual(new DateTime(2019, 1, 1), remoteUser2.Created); //verify that new third user still stored remotely remoteUser3 = await remoteDb.Users.Include(_ => _.Posts).FirstAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual("user3", remoteUser3.Name); Assert.AreEqual(new DateTime(2019, 1, 1), remoteUser3.Created); //verify that user posts are still stored remotely Assert.AreEqual("This is first post from user 1", remoteUser.Posts.OrderBy(_ => _.Updated).ToArray()[0].Content); Assert.AreEqual("This is second post from user 1", remoteUser.Posts.OrderBy(_ => _.Updated).ToArray()[1].Content); Assert.AreEqual(1, remoteUser2.Posts.Count); Assert.AreEqual("This is first post from user 2", remoteUser2.Posts.OrderBy(_ => _.Updated).ToArray()[0].Content); //now delete a post locally localDb.Posts.Remove(localUser2.Posts.First()); await localDb.SaveChangesAsync(); await syncAgent.SynchronizeAsync(); remoteDb = remoteDb.Refresh(); remoteUser2 = await remoteDb.Users.Include(_ => _.Posts).FirstAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual(0, remoteUser2.Posts.Count); //now delete a post on server remoteDb.Posts.Remove(remoteUser.Posts[0]); await remoteDb.SaveChangesAsync(); await syncAgent.SynchronizeAsync(); localDb = localDb.Refresh(); localUser = await localDb.Users.Include(_ => _.Posts).FirstAsync(_ => _.Email == "*****@*****.**"); Assert.AreEqual(1, localUser.Posts.Count); remoteUser2.Posts.Add(new Post() { Content = "Post add to remote user while user is delete on local db", Updated = DateTime.Now }); remoteUser2.Name = "edited name"; await remoteDb.SaveChangesAsync(); localDb.Users.Remove(localUser2); await localDb.SaveChangesAsync(); await syncAgent.SynchronizeAsync(conflictResolutionOnLocalStore : ConflictResolution.ForceWrite); remoteDb = remoteDb.Refresh(); localDb = localDb.Refresh(); //ensure that local db updated (local user 2 is present) localUser2 = await localDb.Users.Include(_ => _.Posts).FirstAsync(_ => _.Email == "*****@*****.**"); localUser2.Posts.Count.ShouldBe(1); localUser2.Posts[0].Content.ShouldBe("Post add to remote user while user is delete on local db"); //ensure that remote db is updated remoteUser2 = await remoteDb.Users.Include(_ => _.Posts).FirstAsync(_ => _.Email == "*****@*****.**"); remoteUser2.Posts.Count.ShouldBe(1); remoteUser2.Posts[0].Content.ShouldBe("Post add to remote user while user is delete on local db"); }