115 lines
5.4 KiB
C#
115 lines
5.4 KiB
C#
using MongoDB.Entities;
|
|||
|
|
|
||
|
|
using PrivaPub.ClientModels.Post;
|
||
|
|
using PrivaPub.Models.Post;
|
||
|
|
using PrivaPub.Models.Social;
|
||
|
|
using PrivaPub.Tests.Support;
|
||
|
|
|
||
|
|
using System.Text.Json.Nodes;
|
||
|
|
|
||
|
|
namespace PrivaPub.Tests.Federation
|
||
|
|
{
|
||
|
|
[Trait("Category", "Integration")]
|
||
|
|
public sealed class CommunityModerationTests : IAsyncLifetime
|
||
|
|
{
|
||
|
|
Harness _harness;
|
||
|
|
|
||
|
|
public async ValueTask InitializeAsync()
|
||
|
|
{
|
||
|
|
Assert.SkipUnless(MongoFixture.Enabled, MongoFixture.Skip);
|
||
|
|
_harness = await Harness.Start();
|
||
|
|
}
|
||
|
|
|
||
|
|
public async ValueTask DisposeAsync()
|
||
|
|
{
|
||
|
|
if (_harness != default)
|
||
|
|
await _harness.DisposeAsync();
|
||
|
|
}
|
||
|
|
|
||
|
|
static string Origin(string id) => new Uri(id).GetLeftPart(UriPartial.Authority);
|
||
|
|
|
||
|
|
// a Lemmy community on the peer's first origin whose moderators collection lists the given accounts
|
||
|
|
RemoteActor Community(params RemoteActor[] moderators)
|
||
|
|
{
|
||
|
|
var community = new RemoteActor(_harness.Peer, "comm", type: "Group");
|
||
|
|
var document = community.Document();
|
||
|
|
document["attributedTo"] = community.Id + "/moderators";
|
||
|
|
_harness.Peer.Serve($"/users/{community.Name}", document.ToJsonString());
|
||
|
|
_harness.Peer.Serve($"/users/{community.Name}/moderators", new JsonObject
|
||
|
|
{
|
||
|
|
["id"] = community.Id + "/moderators", ["type"] = "OrderedCollection",
|
||
|
|
["orderedItems"] = new JsonArray(moderators.Select(m => (JsonNode)m.Id).ToArray())
|
||
|
|
}.ToJsonString());
|
||
|
|
return community;
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task<(Post Post, PrivaPub.Federation.Actors.LocalActor Alice)> PostIn(RemoteActor community)
|
||
|
|
{
|
||
|
|
var token = TestContext.Current.CancellationToken;
|
||
|
|
var (root, alice) = await _harness.Persona("alice");
|
||
|
|
await _harness.Posts.InsertPost(root, new InsertPostForm { AvatarId = alice.Id, Text = "in the community" }, token);
|
||
|
|
var post = await DB.Default.Find<Post>().Match(p => p.GroupUserId == alice.Id).ExecuteFirstAsync(token);
|
||
|
|
await DB.Default.Update<Post>().MatchID(post.ID).Modify(p => p.AudienceURI, community.Id).ExecuteAsync(token);
|
||
|
|
await _harness.Remote.GetActor(community.Id, refresh: true, token);
|
||
|
|
return (post, alice);
|
||
|
|
}
|
||
|
|
|
||
|
|
JsonObject Warn(RemoteActor moderator, RemoteActor community, string persona, string post) => new()
|
||
|
|
{
|
||
|
|
["id"] = $"{Origin(moderator.Id)}/activities/warn/{Guid.NewGuid():N}", ["type"] = "Warn", ["actor"] = moderator.Id,
|
||
|
|
["to"] = new JsonArray(persona), ["object"] = post, ["summary"] = "please keep it civil", ["audience"] = community.Id
|
||
|
|
};
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task A_warning_from_the_communitys_moderators_reaches_the_persona_and_a_strangers_does_not()
|
||
|
|
{
|
||
|
|
var token = TestContext.Current.CancellationToken;
|
||
|
|
var listed = new RemoteActor(_harness.Peer, "listedmod", origin: _harness.Peer.B);
|
||
|
|
var stranger = new RemoteActor(_harness.Peer, "stranger", origin: _harness.Peer.B);
|
||
|
|
var community = Community(listed);
|
||
|
|
var admin = new RemoteActor(_harness.Peer, "admin");
|
||
|
|
var (post, alice) = await PostIn(community);
|
||
|
|
|
||
|
|
await _harness.Deliver(stranger, "/human-centipede", Warn(stranger, community, alice.Uri, post.ObjectURI));
|
||
|
|
Assert.False(await DB.Default.Find<Notification>().Match(n => n.AvatarId == alice.Id && n.Type == NotificationType.ModerationWarning).ExecuteAnyAsync(token));
|
||
|
|
|
||
|
|
await _harness.Deliver(listed, "/human-centipede", Warn(listed, community, alice.Uri, post.ObjectURI));
|
||
|
|
var warning = await DB.Default.Find<Notification>().Match(n => n.AvatarId == alice.Id && n.Type == NotificationType.ModerationWarning).ExecuteFirstAsync(token);
|
||
|
|
Assert.Equal("please keep it civil", warning.Text);
|
||
|
|
Assert.Equal(post.ID, warning.PostId);
|
||
|
|
|
||
|
|
var adminWarning = Warn(admin, community, alice.Uri, post.ObjectURI);
|
||
|
|
adminWarning["summary"] = "from the server's admin";
|
||
|
|
await DB.Default.DeleteAsync<Notification>(n => n.AvatarId == alice.Id);
|
||
|
|
await _harness.Deliver(admin, "/human-centipede", adminWarning);
|
||
|
|
Assert.Equal("from the server's admin", (await DB.Default.Find<Notification>().Match(n => n.AvatarId == alice.Id && n.Type == NotificationType.ModerationWarning).ExecuteFirstAsync(token)).Text);
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task A_reports_resolution_is_kept_only_from_the_server_holding_what_was_reported()
|
||
|
|
{
|
||
|
|
var token = TestContext.Current.CancellationToken;
|
||
|
|
var community = Community();
|
||
|
|
var (post, _) = await PostIn(community);
|
||
|
|
var report = new Report { PostIds = new() { post.ID }, Comment = "spam" };
|
||
|
|
await DB.Default.SaveAsync(report, token);
|
||
|
|
var flag = $"{_harness.Local.BaseAddress}/peasants/privapub_reports/grunts/flag-{report.ID}-1";
|
||
|
|
JsonObject Resolve(RemoteActor by) => new()
|
||
|
|
{
|
||
|
|
["id"] = $"{Origin(by.Id)}/activities/resolve/{Guid.NewGuid():N}", ["type"] = "Resolve", ["actor"] = by.Id, ["to"] = new JsonArray(community.Id),
|
||
|
|
["object"] = new JsonObject { ["id"] = flag, ["type"] = "Flag", ["actor"] = $"{_harness.Local.BaseAddress}/peasants/privapub_reports", ["object"] = post.ObjectURI }
|
||
|
|
};
|
||
|
|
var elsewhere = new RemoteActor(_harness.Peer, "elsewhere", origin: _harness.Peer.B);
|
||
|
|
var moderator = new RemoteActor(_harness.Peer, "moderator");
|
||
|
|
|
||
|
|
await _harness.Deliver(elsewhere, "/human-centipede", Resolve(elsewhere));
|
||
|
|
Assert.Null((await DB.Default.Find<Report>().OneAsync(report.ID, token)).RemoteResolvedAt);
|
||
|
|
|
||
|
|
await _harness.Deliver(moderator, "/human-centipede", Resolve(moderator));
|
||
|
|
var resolved = await DB.Default.Find<Report>().OneAsync(report.ID, token);
|
||
|
|
Assert.NotNull(resolved.RemoteResolvedAt);
|
||
|
|
Assert.Equal(moderator.Id, resolved.RemoteResolvedBy);
|
||
|
|
Assert.False(resolved.IsResolved);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|