diff --git a/src/BirdsiteLive.Domain/BusinessUseCases/ProcessUnfollowUser.cs b/src/BirdsiteLive.Domain/BusinessUseCases/ProcessUnfollowUser.cs index 4d5483a..a8a53b4 100644 --- a/src/BirdsiteLive.Domain/BusinessUseCases/ProcessUnfollowUser.cs +++ b/src/BirdsiteLive.Domain/BusinessUseCases/ProcessUnfollowUser.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Linq; +using System.Threading.Tasks; using BirdsiteLive.DAL.Contracts; namespace BirdsiteLive.Domain.BusinessUseCases @@ -38,8 +39,16 @@ namespace BirdsiteLive.Domain.BusinessUseCases if (follower.FollowingsSyncStatus.ContainsKey(twitterUserId)) follower.FollowingsSyncStatus.Remove(twitterUserId); - // Save Follower - await _followerDal.UpdateFollowerAsync(follower); + // Save or delete Follower + if (follower.Followings.Any()) + await _followerDal.UpdateFollowerAsync(follower); + else + await _followerDal.DeleteFollowerAsync(followerUsername, followerDomain); + + // Check if TwitterUser has still followers + var followers = await _followerDal.GetFollowersAsync(twitterUser.Id); + if (!followers.Any()) + await _twitterUserDal.DeleteTwitterUserAsync(twitterUsername); } } } \ No newline at end of file diff --git a/src/BirdsiteLive.Domain/UserService.cs b/src/BirdsiteLive.Domain/UserService.cs index 221ff4f..42feb39 100644 --- a/src/BirdsiteLive.Domain/UserService.cs +++ b/src/BirdsiteLive.Domain/UserService.cs @@ -137,7 +137,7 @@ namespace BirdsiteLive.Domain if (!sigValidation.SignatureIsValidated) return false; // Save Follow in DB - var followerUserName = sigValidation.User.name.ToLowerInvariant(); + var followerUserName = sigValidation.User.preferredUsername.ToLowerInvariant(); var followerHost = sigValidation.User.url.Replace("https://", string.Empty).Split('/').First(); //var followerInbox = sigValidation.User.inbox; var twitterUser = activity.apObject.apObject.Split('/').Last().Replace("@", string.Empty); diff --git a/src/BirdsiteLive/BirdsiteLive.csproj b/src/BirdsiteLive/BirdsiteLive.csproj index 774c4ce..82bb9ee 100644 --- a/src/BirdsiteLive/BirdsiteLive.csproj +++ b/src/BirdsiteLive/BirdsiteLive.csproj @@ -4,7 +4,7 @@ netcoreapp3.1 d21486de-a812-47eb-a419-05682bb68856 Linux - 0.2.0 + 0.3.0 diff --git a/src/Tests/BirdsiteLive.Domain.Tests/BirdsiteLive.Domain.Tests.csproj b/src/Tests/BirdsiteLive.Domain.Tests/BirdsiteLive.Domain.Tests.csproj index 8762be8..72a94c9 100644 --- a/src/Tests/BirdsiteLive.Domain.Tests/BirdsiteLive.Domain.Tests.csproj +++ b/src/Tests/BirdsiteLive.Domain.Tests/BirdsiteLive.Domain.Tests.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Tests/BirdsiteLive.Domain.Tests/BusinessUseCases/ProcessFollowUserTests.cs b/src/Tests/BirdsiteLive.Domain.Tests/BusinessUseCases/ProcessFollowUserTests.cs new file mode 100644 index 0000000..d5d735e --- /dev/null +++ b/src/Tests/BirdsiteLive.Domain.Tests/BusinessUseCases/ProcessFollowUserTests.cs @@ -0,0 +1,149 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using BirdsiteLive.DAL.Contracts; +using BirdsiteLive.DAL.Models; +using BirdsiteLive.Domain.BusinessUseCases; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using Org.BouncyCastle.Crypto.Prng; + +namespace BirdsiteLive.Domain.Tests.BusinessUseCases +{ + [TestClass] + public class ProcessFollowUserTests + { + [TestMethod] + public async Task ExecuteAsync_UserDontExists_TwitterDontExists_Test() + { + #region Stubs + var username = "testest"; + var domain = "m.s"; + var twitterName = "handle"; + var followerInbox = "/user/testest"; + var inbox = "/inbox"; + + var follower = new Follower + { + Id = 1, + Acct = username, + Host = domain, + SharedInboxRoute = followerInbox, + InboxRoute = inbox, + Followings = new List(), + FollowingsSyncStatus = new Dictionary() + }; + + var twitterUser = new SyncTwitterUser + { + Id = 2, + Acct = twitterName, + LastTweetPostedId = -1, + LastTweetSynchronizedForAllFollowersId = -1 + }; + #endregion + + #region Mocks + var followersDalMock = new Mock(MockBehavior.Strict); + followersDalMock + .SetupSequence(x => x.GetFollowerAsync(username, domain)) + .ReturnsAsync((Follower)null) + .ReturnsAsync(follower); + + followersDalMock + .Setup(x => x.CreateFollowerAsync( + It.Is(y => y == username), + It.Is(y => y == domain), + It.Is(y => y == followerInbox), + It.Is(y => y == inbox), + null, + null)) + .Returns(Task.CompletedTask); + + followersDalMock + .Setup(x => x.UpdateFollowerAsync( + It.Is(y => y.Followings.Contains(twitterUser.Id) + && y.FollowingsSyncStatus[twitterUser.Id] == -1) + )) + .Returns(Task.CompletedTask); + + var twitterUserDalMock = new Mock(MockBehavior.Strict); + twitterUserDalMock + .SetupSequence(x => x.GetTwitterUserAsync(twitterName)) + .ReturnsAsync((SyncTwitterUser)null) + .ReturnsAsync(twitterUser); + + twitterUserDalMock + .Setup(x => x.CreateTwitterUserAsync( + It.Is(y => y == twitterName), + It.Is(y => y == -1))) + .Returns(Task.CompletedTask); + #endregion + + var action = new ProcessFollowUser(followersDalMock.Object, twitterUserDalMock.Object); + await action.ExecuteAsync(username, domain, twitterName, followerInbox, inbox); + + #region Validations + followersDalMock.VerifyAll(); + twitterUserDalMock.VerifyAll(); + #endregion + } + + [TestMethod] + public async Task ExecuteAsync_UserExists_TwitterExists_Test() + { + #region Stubs + var username = "testest"; + var domain = "m.s"; + var twitterName = "handle"; + var followerInbox = "/user/testest"; + var inbox = "/inbox"; + + var follower = new Follower + { + Id = 1, + Acct = username, + Host = domain, + SharedInboxRoute = followerInbox, + InboxRoute = inbox, + Followings = new List(), + FollowingsSyncStatus = new Dictionary() + }; + + var twitterUser = new SyncTwitterUser + { + Id = 2, + Acct = twitterName, + LastTweetPostedId = -1, + LastTweetSynchronizedForAllFollowersId = -1 + }; + #endregion + + #region Mocks + var followersDalMock = new Mock(MockBehavior.Strict); + followersDalMock + .Setup(x => x.GetFollowerAsync(username, domain)) + .ReturnsAsync(follower); + + followersDalMock + .Setup(x => x.UpdateFollowerAsync( + It.Is(y => y.Followings.Contains(twitterUser.Id) + && y.FollowingsSyncStatus[twitterUser.Id] == -1) + )) + .Returns(Task.CompletedTask); + + var twitterUserDalMock = new Mock(MockBehavior.Strict); + twitterUserDalMock + .Setup(x => x.GetTwitterUserAsync(twitterName)) + .ReturnsAsync(twitterUser); + #endregion + + var action = new ProcessFollowUser(followersDalMock.Object, twitterUserDalMock.Object); + await action.ExecuteAsync(username, domain, twitterName, followerInbox, inbox); + + #region Validations + followersDalMock.VerifyAll(); + twitterUserDalMock.VerifyAll(); + #endregion + } + } +} \ No newline at end of file diff --git a/src/Tests/BirdsiteLive.Domain.Tests/BusinessUseCases/ProcessUnfollowUserTests.cs b/src/Tests/BirdsiteLive.Domain.Tests/BusinessUseCases/ProcessUnfollowUserTests.cs new file mode 100644 index 0000000..9bd83e3 --- /dev/null +++ b/src/Tests/BirdsiteLive.Domain.Tests/BusinessUseCases/ProcessUnfollowUserTests.cs @@ -0,0 +1,210 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using BirdsiteLive.DAL.Contracts; +using BirdsiteLive.DAL.Models; +using BirdsiteLive.Domain.BusinessUseCases; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace BirdsiteLive.Domain.Tests.BusinessUseCases +{ + [TestClass] + public class ProcessUnfollowUserTests + { + [TestMethod] + public async Task ExecuteAsync_NoFollowerFound_Test() + { + #region Stubs + var username = "testest"; + var domain = "m.s"; + var twitterName = "handle"; + #endregion + + #region Mocks + var followersDalMock = new Mock(MockBehavior.Strict); + followersDalMock + .Setup(x => x.GetFollowerAsync(username, domain)) + .ReturnsAsync((Follower) null); + + var twitterUserDalMock = new Mock(MockBehavior.Strict); + #endregion + + var action = new ProcessUndoFollowUser(followersDalMock.Object, twitterUserDalMock.Object); + await action.ExecuteAsync(username, domain, twitterName ); + + #region Validations + followersDalMock.VerifyAll(); + twitterUserDalMock.VerifyAll(); + #endregion + } + + [TestMethod] + public async Task ExecuteAsync_NoTwitterUserFound_Test() + { + #region Stubs + var username = "testest"; + var domain = "m.s"; + var twitterName = "handle"; + + var follower = new Follower + { + Id = 1, + Acct = username, + Host = domain, + Followings = new List(), + FollowingsSyncStatus = new Dictionary() + }; + #endregion + + #region Mocks + var followersDalMock = new Mock(MockBehavior.Strict); + followersDalMock + .Setup(x => x.GetFollowerAsync(username, domain)) + .ReturnsAsync(follower); + + var twitterUserDalMock = new Mock(MockBehavior.Strict); + twitterUserDalMock + .Setup(x => x.GetTwitterUserAsync(twitterName)) + .ReturnsAsync((SyncTwitterUser)null); + #endregion + + var action = new ProcessUndoFollowUser(followersDalMock.Object, twitterUserDalMock.Object); + await action.ExecuteAsync(username, domain, twitterName); + + #region Validations + followersDalMock.VerifyAll(); + twitterUserDalMock.VerifyAll(); + #endregion + } + + [TestMethod] + public async Task ExecuteAsync_MultiFollows_Test() + { + #region Stubs + var username = "testest"; + var domain = "m.s"; + var twitterName = "handle"; + + var follower = new Follower + { + Id = 1, + Acct = username, + Host = domain, + Followings = new List { 2, 3 }, + FollowingsSyncStatus = new Dictionary { { 2, 460 }, { 3, 563} } + }; + + var twitterUser = new SyncTwitterUser + { + Id = 2, + Acct = twitterName, + LastTweetPostedId = 460, + LastTweetSynchronizedForAllFollowersId = 460 + }; + + var followerList = new List + { + new Follower(), + new Follower() + }; + #endregion + + #region Mocks + var followersDalMock = new Mock(MockBehavior.Strict); + followersDalMock + .Setup(x => x.GetFollowerAsync(username, domain)) + .ReturnsAsync(follower); + + followersDalMock + .Setup(x => x.UpdateFollowerAsync( + It.Is(y => !y.Followings.Contains(twitterUser.Id) + && !y.FollowingsSyncStatus.ContainsKey(twitterUser.Id)) + )) + .Returns(Task.CompletedTask); + + followersDalMock + .Setup(x => x.GetFollowersAsync(twitterUser.Id)) + .ReturnsAsync(followerList.ToArray()); + + var twitterUserDalMock = new Mock(MockBehavior.Strict); + twitterUserDalMock + .Setup(x => x.GetTwitterUserAsync(twitterName)) + .ReturnsAsync(twitterUser); + #endregion + + var action = new ProcessUndoFollowUser(followersDalMock.Object, twitterUserDalMock.Object); + await action.ExecuteAsync(username, domain, twitterName); + + #region Validations + followersDalMock.VerifyAll(); + twitterUserDalMock.VerifyAll(); + #endregion + } + + [TestMethod] + public async Task ExecuteAsync_CleanUp_Test() + { + #region Stubs + var username = "testest"; + var domain = "m.s"; + var twitterName = "handle"; + + var follower = new Follower + { + Id = 1, + Acct = username, + Host = domain, + Followings = new List { 2 }, + FollowingsSyncStatus = new Dictionary { { 2, 460 } } + }; + + var twitterUser = new SyncTwitterUser + { + Id = 2, + Acct = twitterName, + LastTweetPostedId = 460, + LastTweetSynchronizedForAllFollowersId = 460 + }; + + var followerList = new List(); + #endregion + + #region Mocks + var followersDalMock = new Mock(MockBehavior.Strict); + followersDalMock + .Setup(x => x.GetFollowerAsync(username, domain)) + .ReturnsAsync(follower); + + followersDalMock + .Setup(x => x.DeleteFollowerAsync( + It.Is(y => y == username), + It.Is(y => y == domain) + )) + .Returns(Task.CompletedTask); + + followersDalMock + .Setup(x => x.GetFollowersAsync(twitterUser.Id)) + .ReturnsAsync(followerList.ToArray()); + + var twitterUserDalMock = new Mock(MockBehavior.Strict); + twitterUserDalMock + .Setup(x => x.GetTwitterUserAsync(twitterName)) + .ReturnsAsync(twitterUser); + + twitterUserDalMock + .Setup(x => x.DeleteTwitterUserAsync( + It.Is(y => y == twitterName) + )) + .Returns(Task.CompletedTask); + #endregion + + var action = new ProcessUndoFollowUser(followersDalMock.Object, twitterUserDalMock.Object); + await action.ExecuteAsync(username, domain, twitterName); + + #region Validations + followersDalMock.VerifyAll(); + twitterUserDalMock.VerifyAll(); + #endregion + } + } +} \ No newline at end of file