Merge pull request #64 from NicolasConstant/topic_reply-publication

Topic reply publication
This commit is contained in:
Nicolas Constant 2021-01-23 01:00:45 +01:00 committed by GitHub
commit ffdd041a8c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 923 additions and 18 deletions

View file

@ -10,4 +10,5 @@ You can configure some of BirdsiteLIVE's settings via environment variables (tho
## Instance customization
* `Instance:Name` (default: BirdsiteLIVE) the name of the instance
* `Instance:ResolveMentionsInProfiles` (default: true) to enable or disable mentions parsing in profile's description. Resolving it will consume more User's API calls since newly discovered account can also contain references to others accounts as well. On a big instance it is recommended to disable it.
* `Instance:ResolveMentionsInProfiles` (default: true) to enable or disable mentions parsing in profile's description. Resolving it will consume more User's API calls since newly discovered account can also contain references to others accounts as well. On a big instance it is recommended to disable it.
* `Instance:PublishReplies` (default: false) to enable or disable replies publishing.

View file

@ -6,5 +6,6 @@
public string Domain { get; set; }
public string AdminEmail { get; set; }
public bool ResolveMentionsInProfiles { get; set; }
public bool PublishReplies { get; set; }
}
}

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using BirdsiteLive.Common.Settings;
using BirdsiteLive.DAL.Contracts;
using BirdsiteLive.DAL.Models;
using BirdsiteLive.Domain;
@ -21,15 +22,17 @@ namespace BirdsiteLive.Pipeline.Processors.SubTasks
private readonly IActivityPubService _activityPubService;
private readonly IStatusService _statusService;
private readonly IFollowersDal _followersDal;
private readonly InstanceSettings _settings;
private readonly ILogger<SendTweetsToInboxTask> _logger;
#region Ctor
public SendTweetsToInboxTask(IActivityPubService activityPubService, IStatusService statusService, IFollowersDal followersDal, ILogger<SendTweetsToInboxTask> logger)
public SendTweetsToInboxTask(IActivityPubService activityPubService, IStatusService statusService, IFollowersDal followersDal, InstanceSettings settings, ILogger<SendTweetsToInboxTask> logger)
{
_activityPubService = activityPubService;
_statusService = statusService;
_followersDal = followersDal;
_settings = settings;
_logger = logger;
}
#endregion
@ -52,8 +55,13 @@ namespace BirdsiteLive.Pipeline.Processors.SubTasks
{
try
{
var note = _statusService.GetStatus(user.Acct, tweet);
await _activityPubService.PostNewNoteActivity(note, user.Acct, tweet.Id.ToString(), follower.Host, inbox);
if (!tweet.IsReply ||
tweet.IsReply && tweet.IsThread ||
_settings.PublishReplies)
{
var note = _statusService.GetStatus(user.Acct, tweet);
await _activityPubService.PostNewNoteActivity(note, user.Acct, tweet.Id.ToString(), follower.Host, inbox);
}
}
catch (ArgumentException e)
{

View file

@ -2,6 +2,7 @@
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using BirdsiteLive.Common.Settings;
using BirdsiteLive.DAL.Contracts;
using BirdsiteLive.DAL.Models;
using BirdsiteLive.Domain;
@ -20,14 +21,16 @@ namespace BirdsiteLive.Pipeline.Processors.SubTasks
private readonly IStatusService _statusService;
private readonly IActivityPubService _activityPubService;
private readonly IFollowersDal _followersDal;
private readonly InstanceSettings _settings;
private readonly ILogger<SendTweetsToSharedInboxTask> _logger;
#region Ctor
public SendTweetsToSharedInboxTask(IActivityPubService activityPubService, IStatusService statusService, IFollowersDal followersDal, ILogger<SendTweetsToSharedInboxTask> logger)
public SendTweetsToSharedInboxTask(IActivityPubService activityPubService, IStatusService statusService, IFollowersDal followersDal, InstanceSettings settings, ILogger<SendTweetsToSharedInboxTask> logger)
{
_activityPubService = activityPubService;
_statusService = statusService;
_followersDal = followersDal;
_settings = settings;
_logger = logger;
}
#endregion
@ -52,8 +55,13 @@ namespace BirdsiteLive.Pipeline.Processors.SubTasks
{
try
{
var note = _statusService.GetStatus(user.Acct, tweet);
await _activityPubService.PostNewNoteActivity(note, user.Acct, tweet.Id.ToString(), host, inbox);
if (!tweet.IsReply ||
tweet.IsReply && tweet.IsThread ||
_settings.PublishReplies)
{
var note = _statusService.GetStatus(user.Acct, tweet);
await _activityPubService.PostNewNoteActivity(note, user.Acct, tweet.Id.ToString(), host, inbox);
}
}
catch (ArgumentException e)
{
@ -66,7 +74,7 @@ namespace BirdsiteLive.Pipeline.Processors.SubTasks
throw;
}
}
syncStatus = tweet.Id;
}
}

View file

@ -24,7 +24,9 @@ namespace BirdsiteLive.Twitter.Extractors
InReplyToAccount = tweet.InReplyToScreenName,
MessageContent = ExtractMessage(tweet),
Media = ExtractMedia(tweet.Media),
CreatedAt = tweet.CreatedAt.ToUniversalTime()
CreatedAt = tweet.CreatedAt.ToUniversalTime(),
IsReply = tweet.InReplyToUserId != null,
IsThread = tweet.InReplyToUserId != null && tweet.InReplyToUserId == tweet.CreatedBy.Id
};
return extractedTweet;
}

View file

@ -11,5 +11,7 @@ namespace BirdsiteLive.Twitter.Models
public ExtractedMedia[] Media { get; set; }
public DateTime CreatedAt { get; set; }
public string InReplyToAccount { get; set; }
public bool IsReply { get; set; }
public bool IsThread { get; set; }
}
}

View file

@ -40,7 +40,7 @@ namespace BirdsiteLive.Twitter
var tweet = Tweet.GetTweet(statusId);
_statisticsHandler.CalledTweetApi();
if (tweet == null) return null; //TODO: test this
return _tweetExtractor.Extract(tweet);
return _tweetExtractor.Extract(tweet);
}
public ExtractedTweet[] GetTimeline(string username, int nberTweets, long fromTweetId = -1)

View file

@ -13,7 +13,8 @@
"Name": "BirdsiteLIVE",
"Domain": "domain.name",
"AdminEmail": "me@domain.name",
"ResolveMentionsInProfiles": true
"ResolveMentionsInProfiles": true,
"PublishReplies": false
},
"Db": {
"Type": "postgres",

View file

@ -76,7 +76,7 @@ namespace BirdsiteLive.Pipeline.Tests.Processors
processor.WaitFactor = 2;
processor.GetTwitterUsersAsync(buffer, CancellationToken.None);
await Task.Delay(200);
await Task.Delay(300);
#region Validations
twitterUserDalMock.VerifyAll();

View file

@ -4,6 +4,7 @@ using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using BirdsiteLive.ActivityPub.Models;
using BirdsiteLive.Common.Settings;
using BirdsiteLive.DAL.Contracts;
using BirdsiteLive.DAL.Models;
using BirdsiteLive.Domain;
@ -54,6 +55,11 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
InboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
};
var settings = new InstanceSettings
{
PublishReplies = false
};
#endregion
#region Mocks
@ -83,7 +89,239 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
var loggerMock = new Mock<ILogger<SendTweetsToInboxTask>>();
#endregion
var task = new SendTweetsToInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, loggerMock.Object);
var task = new SendTweetsToInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
await task.ExecuteAsync(tweets.ToArray(), follower, twitterUser);
#region Validations
activityPubService.VerifyAll();
statusServiceMock.VerifyAll();
followersDalMock.VerifyAll();
#endregion
}
[TestMethod]
public async Task ExecuteAsync_SingleTweet_Reply_Test()
{
#region Stubs
var tweetId = 10;
var tweets = new List<ExtractedTweet>
{
new ExtractedTweet
{
Id = tweetId,
IsReply = true,
IsThread = false
}
};
var noteId = "noteId";
var note = new Note()
{
id = noteId
};
var twitterHandle = "Test";
var twitterUserId = 7;
var twitterUser = new SyncTwitterUser
{
Id = twitterUserId,
Acct = twitterHandle
};
var host = "domain.ext";
var inbox = "/user/inbox";
var follower = new Follower
{
Id = 1,
Host = host,
InboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
};
var settings = new InstanceSettings
{
PublishReplies = false
};
#endregion
#region Mocks
var activityPubService = new Mock<IActivityPubService>(MockBehavior.Strict);
var statusServiceMock = new Mock<IStatusService>(MockBehavior.Strict);
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
followersDalMock
.Setup(x => x.UpdateFollowerAsync(
It.Is<Follower>(y => y.Id == follower.Id && y.FollowingsSyncStatus[twitterUserId] == tweetId)))
.Returns(Task.CompletedTask);
var loggerMock = new Mock<ILogger<SendTweetsToInboxTask>>();
#endregion
var task = new SendTweetsToInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
await task.ExecuteAsync(tweets.ToArray(), follower, twitterUser);
#region Validations
activityPubService.VerifyAll();
statusServiceMock.VerifyAll();
followersDalMock.VerifyAll();
#endregion
}
[TestMethod]
public async Task ExecuteAsync_SingleTweet_ReplyThread_Test()
{
#region Stubs
var tweetId = 10;
var tweets = new List<ExtractedTweet>
{
new ExtractedTweet
{
Id = tweetId,
IsReply = true,
IsThread = true
}
};
var noteId = "noteId";
var note = new Note()
{
id = noteId
};
var twitterHandle = "Test";
var twitterUserId = 7;
var twitterUser = new SyncTwitterUser
{
Id = twitterUserId,
Acct = twitterHandle
};
var host = "domain.ext";
var inbox = "/user/inbox";
var follower = new Follower
{
Id = 1,
Host = host,
InboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
};
var settings = new InstanceSettings
{
PublishReplies = false
};
#endregion
#region Mocks
var activityPubService = new Mock<IActivityPubService>(MockBehavior.Strict);
activityPubService
.Setup(x => x.PostNewNoteActivity(
It.Is<Note>(y => y.id == noteId),
It.Is<string>(y => y == twitterHandle),
It.Is<string>(y => y == tweetId.ToString()),
It.Is<string>(y => y == host),
It.Is<string>(y => y == inbox)))
.Returns(Task.CompletedTask);
var statusServiceMock = new Mock<IStatusService>(MockBehavior.Strict);
statusServiceMock
.Setup(x => x.GetStatus(
It.Is<string>(y => y == twitterHandle),
It.Is<ExtractedTweet>(y => y.Id == tweetId)))
.Returns(note);
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
followersDalMock
.Setup(x => x.UpdateFollowerAsync(
It.Is<Follower>(y => y.Id == follower.Id && y.FollowingsSyncStatus[twitterUserId] == tweetId)))
.Returns(Task.CompletedTask);
var loggerMock = new Mock<ILogger<SendTweetsToInboxTask>>();
#endregion
var task = new SendTweetsToInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
await task.ExecuteAsync(tweets.ToArray(), follower, twitterUser);
#region Validations
activityPubService.VerifyAll();
statusServiceMock.VerifyAll();
followersDalMock.VerifyAll();
#endregion
}
[TestMethod]
public async Task ExecuteAsync_SingleTweet_PublishReply_Test()
{
#region Stubs
var tweetId = 10;
var tweets = new List<ExtractedTweet>
{
new ExtractedTweet
{
Id = tweetId,
IsReply = true,
IsThread = false
}
};
var noteId = "noteId";
var note = new Note()
{
id = noteId
};
var twitterHandle = "Test";
var twitterUserId = 7;
var twitterUser = new SyncTwitterUser
{
Id = twitterUserId,
Acct = twitterHandle
};
var host = "domain.ext";
var inbox = "/user/inbox";
var follower = new Follower
{
Id = 1,
Host = host,
InboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
};
var settings = new InstanceSettings
{
PublishReplies = true
};
#endregion
#region Mocks
var activityPubService = new Mock<IActivityPubService>(MockBehavior.Strict);
activityPubService
.Setup(x => x.PostNewNoteActivity(
It.Is<Note>(y => y.id == noteId),
It.Is<string>(y => y == twitterHandle),
It.Is<string>(y => y == tweetId.ToString()),
It.Is<string>(y => y == host),
It.Is<string>(y => y == inbox)))
.Returns(Task.CompletedTask);
var statusServiceMock = new Mock<IStatusService>(MockBehavior.Strict);
statusServiceMock
.Setup(x => x.GetStatus(
It.Is<string>(y => y == twitterHandle),
It.Is<ExtractedTweet>(y => y.Id == tweetId)))
.Returns(note);
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
followersDalMock
.Setup(x => x.UpdateFollowerAsync(
It.Is<Follower>(y => y.Id == follower.Id && y.FollowingsSyncStatus[twitterUserId] == tweetId)))
.Returns(Task.CompletedTask);
var loggerMock = new Mock<ILogger<SendTweetsToInboxTask>>();
#endregion
var task = new SendTweetsToInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
await task.ExecuteAsync(tweets.ToArray(), follower, twitterUser);
#region Validations
@ -126,6 +364,11 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
InboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 10 } }
};
var settings = new InstanceSettings
{
PublishReplies = false
};
#endregion
#region Mocks
@ -161,7 +404,7 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
var loggerMock = new Mock<ILogger<SendTweetsToInboxTask>>();
#endregion
var task = new SendTweetsToInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, loggerMock.Object);
var task = new SendTweetsToInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
await task.ExecuteAsync(tweets.ToArray(), follower, twitterUser);
#region Validations
@ -205,6 +448,11 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
InboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 10 } }
};
var settings = new InstanceSettings
{
PublishReplies = false
};
#endregion
#region Mocks
@ -247,7 +495,7 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
var loggerMock = new Mock<ILogger<SendTweetsToInboxTask>>();
#endregion
var task = new SendTweetsToInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, loggerMock.Object);
var task = new SendTweetsToInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
try
{
@ -262,5 +510,147 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
#endregion
}
}
[TestMethod]
public async Task ExecuteAsync_SingleTweet_ParsingError_Test()
{
#region Stubs
var tweetId = 10;
var tweets = new List<ExtractedTweet>
{
new ExtractedTweet
{
Id = tweetId,
}
};
var noteId = "noteId";
var note = new Note()
{
id = noteId
};
var twitterHandle = "Test";
var twitterUserId = 7;
var twitterUser = new SyncTwitterUser
{
Id = twitterUserId,
Acct = twitterHandle
};
var host = "domain.ext";
var inbox = "/user/inbox";
var follower = new Follower
{
Id = 1,
Host = host,
InboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
};
var settings = new InstanceSettings
{
PublishReplies = false
};
#endregion
#region Mocks
var activityPubService = new Mock<IActivityPubService>(MockBehavior.Strict);
var statusServiceMock = new Mock<IStatusService>(MockBehavior.Strict);
statusServiceMock
.Setup(x => x.GetStatus(
It.Is<string>(y => y == twitterHandle),
It.Is<ExtractedTweet>(y => y.Id == tweetId)))
.Throws(new ArgumentException("Invalid pattern blabla at offset 9"));
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
followersDalMock
.Setup(x => x.UpdateFollowerAsync(
It.Is<Follower>(y => y.Id == follower.Id && y.FollowingsSyncStatus[twitterUserId] == tweetId)))
.Returns(Task.CompletedTask);
var loggerMock = new Mock<ILogger<SendTweetsToInboxTask>>();
#endregion
var task = new SendTweetsToInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
await task.ExecuteAsync(tweets.ToArray(), follower, twitterUser);
#region Validations
activityPubService.VerifyAll();
statusServiceMock.VerifyAll();
followersDalMock.VerifyAll();
#endregion
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public async Task ExecuteAsync_SingleTweet_ArgumentException_Test()
{
#region Stubs
var tweetId = 10;
var tweets = new List<ExtractedTweet>
{
new ExtractedTweet
{
Id = tweetId,
}
};
var twitterHandle = "Test";
var twitterUserId = 7;
var twitterUser = new SyncTwitterUser
{
Id = twitterUserId,
Acct = twitterHandle
};
var host = "domain.ext";
var inbox = "/user/inbox";
var follower = new Follower
{
Id = 1,
Host = host,
InboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
};
var settings = new InstanceSettings
{
PublishReplies = false
};
#endregion
#region Mocks
var activityPubService = new Mock<IActivityPubService>(MockBehavior.Strict);
var statusServiceMock = new Mock<IStatusService>(MockBehavior.Strict);
statusServiceMock
.Setup(x => x.GetStatus(
It.Is<string>(y => y == twitterHandle),
It.Is<ExtractedTweet>(y => y.Id == tweetId)))
.Throws(new ArgumentException());
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
var loggerMock = new Mock<ILogger<SendTweetsToInboxTask>>();
#endregion
var task = new SendTweetsToInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
try
{
await task.ExecuteAsync(tweets.ToArray(), follower, twitterUser);
}
finally
{
#region Validations
activityPubService.VerifyAll();
statusServiceMock.VerifyAll();
followersDalMock.VerifyAll();
#endregion
}
}
}
}

View file

@ -5,6 +5,7 @@ using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using BirdsiteLive.ActivityPub.Models;
using BirdsiteLive.Common.Settings;
using BirdsiteLive.DAL.Contracts;
using BirdsiteLive.DAL.Models;
using BirdsiteLive.Domain;
@ -72,6 +73,11 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 7 } }
}
};
var settings = new InstanceSettings
{
PublishReplies = false
};
#endregion
#region Mocks
@ -105,7 +111,303 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
var loggerMock = new Mock<ILogger<SendTweetsToSharedInboxTask>>();
#endregion
var task = new SendTweetsToSharedInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, loggerMock.Object);
var task = new SendTweetsToSharedInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
await task.ExecuteAsync(tweets.ToArray(), twitterUser, host, followers.ToArray());
#region Validations
activityPubService.VerifyAll();
statusServiceMock.VerifyAll();
followersDalMock.VerifyAll();
#endregion
}
[TestMethod]
public async Task ExecuteAsync_SingleTweet_Reply_Test()
{
#region Stubs
var tweetId = 10;
var tweets = new List<ExtractedTweet>
{
new ExtractedTweet
{
Id = tweetId,
IsReply = true,
IsThread = false
}
};
var noteId = "noteId";
var note = new Note()
{
id = noteId
};
var twitterHandle = "Test";
var twitterUserId = 7;
var twitterUser = new SyncTwitterUser
{
Id = twitterUserId,
Acct = twitterHandle
};
var host = "domain.ext";
var inbox = "/inbox";
var followers = new List<Follower>
{
new Follower
{
Id = 1,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
},
new Follower
{
Id = 2,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 8 } }
},
new Follower
{
Id = 3,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 7 } }
}
};
var settings = new InstanceSettings
{
PublishReplies = false
};
#endregion
#region Mocks
var activityPubService = new Mock<IActivityPubService>(MockBehavior.Strict);
var statusServiceMock = new Mock<IStatusService>(MockBehavior.Strict);
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
foreach (var follower in followers)
{
followersDalMock
.Setup(x => x.UpdateFollowerAsync(
It.Is<Follower>(y => y.Id == follower.Id && y.FollowingsSyncStatus[twitterUserId] == tweetId)))
.Returns(Task.CompletedTask);
}
var loggerMock = new Mock<ILogger<SendTweetsToSharedInboxTask>>();
#endregion
var task = new SendTweetsToSharedInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
await task.ExecuteAsync(tweets.ToArray(), twitterUser, host, followers.ToArray());
#region Validations
activityPubService.VerifyAll();
statusServiceMock.VerifyAll();
followersDalMock.VerifyAll();
#endregion
}
[TestMethod]
public async Task ExecuteAsync_SingleTweet_ReplyThread_Test()
{
#region Stubs
var tweetId = 10;
var tweets = new List<ExtractedTweet>
{
new ExtractedTweet
{
Id = tweetId,
IsReply = true,
IsThread = true
}
};
var noteId = "noteId";
var note = new Note()
{
id = noteId
};
var twitterHandle = "Test";
var twitterUserId = 7;
var twitterUser = new SyncTwitterUser
{
Id = twitterUserId,
Acct = twitterHandle
};
var host = "domain.ext";
var inbox = "/inbox";
var followers = new List<Follower>
{
new Follower
{
Id = 1,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
},
new Follower
{
Id = 2,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 8 } }
},
new Follower
{
Id = 3,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 7 } }
}
};
var settings = new InstanceSettings
{
PublishReplies = false
};
#endregion
#region Mocks
var activityPubService = new Mock<IActivityPubService>(MockBehavior.Strict);
activityPubService
.Setup(x => x.PostNewNoteActivity(
It.Is<Note>(y => y.id == noteId),
It.Is<string>(y => y == twitterHandle),
It.Is<string>(y => y == tweetId.ToString()),
It.Is<string>(y => y == host),
It.Is<string>(y => y == inbox)))
.Returns(Task.CompletedTask);
var statusServiceMock = new Mock<IStatusService>(MockBehavior.Strict);
statusServiceMock
.Setup(x => x.GetStatus(
It.Is<string>(y => y == twitterHandle),
It.Is<ExtractedTweet>(y => y.Id == tweetId)))
.Returns(note);
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
foreach (var follower in followers)
{
followersDalMock
.Setup(x => x.UpdateFollowerAsync(
It.Is<Follower>(y => y.Id == follower.Id && y.FollowingsSyncStatus[twitterUserId] == tweetId)))
.Returns(Task.CompletedTask);
}
var loggerMock = new Mock<ILogger<SendTweetsToSharedInboxTask>>();
#endregion
var task = new SendTweetsToSharedInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
await task.ExecuteAsync(tweets.ToArray(), twitterUser, host, followers.ToArray());
#region Validations
activityPubService.VerifyAll();
statusServiceMock.VerifyAll();
followersDalMock.VerifyAll();
#endregion
}
[TestMethod]
public async Task ExecuteAsync_SingleTweet_PublishReply_Test()
{
#region Stubs
var tweetId = 10;
var tweets = new List<ExtractedTweet>
{
new ExtractedTweet
{
Id = tweetId,
IsReply = true,
IsThread = false
}
};
var noteId = "noteId";
var note = new Note()
{
id = noteId
};
var twitterHandle = "Test";
var twitterUserId = 7;
var twitterUser = new SyncTwitterUser
{
Id = twitterUserId,
Acct = twitterHandle
};
var host = "domain.ext";
var inbox = "/inbox";
var followers = new List<Follower>
{
new Follower
{
Id = 1,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
},
new Follower
{
Id = 2,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 8 } }
},
new Follower
{
Id = 3,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 7 } }
}
};
var settings = new InstanceSettings
{
PublishReplies = true
};
#endregion
#region Mocks
var activityPubService = new Mock<IActivityPubService>(MockBehavior.Strict);
activityPubService
.Setup(x => x.PostNewNoteActivity(
It.Is<Note>(y => y.id == noteId),
It.Is<string>(y => y == twitterHandle),
It.Is<string>(y => y == tweetId.ToString()),
It.Is<string>(y => y == host),
It.Is<string>(y => y == inbox)))
.Returns(Task.CompletedTask);
var statusServiceMock = new Mock<IStatusService>(MockBehavior.Strict);
statusServiceMock
.Setup(x => x.GetStatus(
It.Is<string>(y => y == twitterHandle),
It.Is<ExtractedTweet>(y => y.Id == tweetId)))
.Returns(note);
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
foreach (var follower in followers)
{
followersDalMock
.Setup(x => x.UpdateFollowerAsync(
It.Is<Follower>(y => y.Id == follower.Id && y.FollowingsSyncStatus[twitterUserId] == tweetId)))
.Returns(Task.CompletedTask);
}
var loggerMock = new Mock<ILogger<SendTweetsToSharedInboxTask>>();
#endregion
var task = new SendTweetsToSharedInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
await task.ExecuteAsync(tweets.ToArray(), twitterUser, host, followers.ToArray());
#region Validations
@ -165,6 +467,11 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
FollowingsSyncStatus = new Dictionary<int, long> {{twitterUserId, 7}}
}
};
var settings = new InstanceSettings
{
PublishReplies = false
};
#endregion
#region Mocks
@ -204,7 +511,7 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
var loggerMock = new Mock<ILogger<SendTweetsToSharedInboxTask>>();
#endregion
var task = new SendTweetsToSharedInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, loggerMock.Object);
var task = new SendTweetsToSharedInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
await task.ExecuteAsync(tweets.ToArray(), twitterUser, host, followers.ToArray());
#region Validations
@ -265,6 +572,11 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
FollowingsSyncStatus = new Dictionary<int, long> {{twitterUserId, 7}}
}
};
var settings = new InstanceSettings
{
PublishReplies = false
};
#endregion
#region Mocks
@ -311,7 +623,7 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
var loggerMock = new Mock<ILogger<SendTweetsToSharedInboxTask>>();
#endregion
var task = new SendTweetsToSharedInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, loggerMock.Object);
var task = new SendTweetsToSharedInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
try
{
@ -326,5 +638,185 @@ namespace BirdsiteLive.Pipeline.Tests.Processors.SubTasks
#endregion
}
}
[TestMethod]
public async Task ExecuteAsync_SingleTweet_ParsingError_Test()
{
#region Stubs
var tweetId = 10;
var tweets = new List<ExtractedTweet>
{
new ExtractedTweet
{
Id = tweetId,
}
};
var noteId = "noteId";
var note = new Note()
{
id = noteId
};
var twitterHandle = "Test";
var twitterUserId = 7;
var twitterUser = new SyncTwitterUser
{
Id = twitterUserId,
Acct = twitterHandle
};
var host = "domain.ext";
var inbox = "/inbox";
var followers = new List<Follower>
{
new Follower
{
Id = 1,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
},
new Follower
{
Id = 2,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 8 } }
},
new Follower
{
Id = 3,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 7 } }
}
};
var settings = new InstanceSettings
{
PublishReplies = false
};
#endregion
#region Mocks
var activityPubService = new Mock<IActivityPubService>(MockBehavior.Strict);
var statusServiceMock = new Mock<IStatusService>(MockBehavior.Strict);
statusServiceMock
.Setup(x => x.GetStatus(
It.Is<string>(y => y == twitterHandle),
It.Is<ExtractedTweet>(y => y.Id == tweetId)))
.Throws(new ArgumentException("Invalid pattern blabla at offset 9"));
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
foreach (var follower in followers)
{
followersDalMock
.Setup(x => x.UpdateFollowerAsync(
It.Is<Follower>(y => y.Id == follower.Id && y.FollowingsSyncStatus[twitterUserId] == tweetId)))
.Returns(Task.CompletedTask);
}
var loggerMock = new Mock<ILogger<SendTweetsToSharedInboxTask>>();
#endregion
var task = new SendTweetsToSharedInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
await task.ExecuteAsync(tweets.ToArray(), twitterUser, host, followers.ToArray());
#region Validations
activityPubService.VerifyAll();
statusServiceMock.VerifyAll();
followersDalMock.VerifyAll();
#endregion
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public async Task ExecuteAsync_SingleTweet_ArgumentException_Test()
{
#region Stubs
var tweetId = 10;
var tweets = new List<ExtractedTweet>
{
new ExtractedTweet
{
Id = tweetId,
}
};
var twitterHandle = "Test";
var twitterUserId = 7;
var twitterUser = new SyncTwitterUser
{
Id = twitterUserId,
Acct = twitterHandle
};
var host = "domain.ext";
var inbox = "/inbox";
var followers = new List<Follower>
{
new Follower
{
Id = 1,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 9 } }
},
new Follower
{
Id = 2,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 8 } }
},
new Follower
{
Id = 3,
Host = host,
SharedInboxRoute = inbox,
FollowingsSyncStatus = new Dictionary<int, long> { { twitterUserId, 7 } }
}
};
var settings = new InstanceSettings
{
PublishReplies = false
};
#endregion
#region Mocks
var activityPubService = new Mock<IActivityPubService>(MockBehavior.Strict);
var statusServiceMock = new Mock<IStatusService>(MockBehavior.Strict);
statusServiceMock
.Setup(x => x.GetStatus(
It.Is<string>(y => y == twitterHandle),
It.Is<ExtractedTweet>(y => y.Id == tweetId)))
.Throws(new ArgumentException());
var followersDalMock = new Mock<IFollowersDal>(MockBehavior.Strict);
var loggerMock = new Mock<ILogger<SendTweetsToSharedInboxTask>>();
#endregion
var task = new SendTweetsToSharedInboxTask(activityPubService.Object, statusServiceMock.Object, followersDalMock.Object, settings, loggerMock.Object);
try
{
await task.ExecuteAsync(tweets.ToArray(), twitterUser, host, followers.ToArray());
}
finally
{
#region Validations
activityPubService.VerifyAll();
statusServiceMock.VerifyAll();
followersDalMock.VerifyAll();
#endregion
}
}
}
}