124 lines
5.1 KiB
C#
124 lines
5.1 KiB
C#
using MongoDB.Entities;
|
|||
|
|
|
||
|
|
using PrivaPub.ClientModels.Social;
|
||
|
|
using PrivaPub.Models.Federation;
|
||
|
|
using PrivaPub.Models.Social;
|
||
|
|
using PrivaPub.Tests.Support;
|
||
|
|
|
||
|
|
using System.Text.Json.Nodes;
|
||
|
|
|
||
|
|
namespace PrivaPub.Tests.Domain
|
||
|
|
{
|
||
|
|
[Trait("Category", "Integration")]
|
||
|
|
public sealed class FollowTests : 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();
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task A_local_follow_is_accepted_at_once_and_notified()
|
||
|
|
{
|
||
|
|
var token = TestContext.Current.CancellationToken;
|
||
|
|
var (root, alice) = await _harness.Persona("alice");
|
||
|
|
var (_, bob) = await _harness.Persona("bob");
|
||
|
|
|
||
|
|
var result = await _harness.Follows.Follow(root, new FollowForm { AvatarId = alice.Id, Target = "@" + bob.UserName }, token);
|
||
|
|
|
||
|
|
Assert.True(result.IsValid);
|
||
|
|
var following = await DB.Default.Find<Following>().Match(f => f.AvatarId == alice.Id).ExecuteSingleAsync(token);
|
||
|
|
Assert.Equal(FollowState.Accepted, following.State);
|
||
|
|
Assert.True(following.TargetIsLocal);
|
||
|
|
Assert.True((await DB.Default.Find<Follower>().Match(f => f.LocalActorId == bob.Id && f.ActorURI == alice.Uri).ExecuteSingleAsync(token)).IsAccepted);
|
||
|
|
Assert.Equal(NotificationType.Follow, (await DB.Default.Find<Notification>().Match(n => n.AvatarId == bob.Id).ExecuteSingleAsync(token)).Type);
|
||
|
|
Assert.Empty(await _harness.Outgoing(bob.Inbox));
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task Someone_elses_persona_cannot_be_made_to_follow()
|
||
|
|
{
|
||
|
|
var (_, alice) = await _harness.Persona("alice");
|
||
|
|
var (_, bob) = await _harness.Persona("bob");
|
||
|
|
|
||
|
|
var result = await _harness.Follows.Follow("someone-else", new FollowForm { AvatarId = alice.Id, Target = bob.UserName }, TestContext.Current.CancellationToken);
|
||
|
|
|
||
|
|
Assert.False(result.IsValid);
|
||
|
|
Assert.Equal(404, result.StatusCode);
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task A_remote_follow_is_requested_then_accepted()
|
||
|
|
{
|
||
|
|
var token = TestContext.Current.CancellationToken;
|
||
|
|
var (root, alice) = await _harness.Persona("alice");
|
||
|
|
var carol = new RemoteActor(_harness.Peer, "carol");
|
||
|
|
|
||
|
|
await _harness.Follows.Follow(root, new FollowForm { AvatarId = alice.Id, Target = carol.Id }, token);
|
||
|
|
|
||
|
|
var following = await DB.Default.Find<Following>().Match(f => f.AvatarId == alice.Id).ExecuteSingleAsync(token);
|
||
|
|
Assert.Equal(FollowState.Requested, following.State);
|
||
|
|
var follow = Assert.Single(await _harness.Outgoing(carol.Id + "/inbox"));
|
||
|
|
Assert.Equal("Follow", follow["type"]!.GetValue<string>());
|
||
|
|
Assert.Equal(alice.Uri, follow["actor"]!.GetValue<string>());
|
||
|
|
Assert.StartsWith(alice.Uri + "/grunts/follow-", follow["id"]!.GetValue<string>());
|
||
|
|
|
||
|
|
var accept = new JsonObject
|
||
|
|
{
|
||
|
|
["id"] = $"{carol.Id}#accepts/{Guid.NewGuid():N}",
|
||
|
|
["type"] = "Accept",
|
||
|
|
["actor"] = carol.Id,
|
||
|
|
["object"] = follow.DeepClone()
|
||
|
|
};
|
||
|
|
Assert.Equal(202, (await _harness.Deliver(carol, $"/peasants/{alice.UserName}/mouth", accept)).StatusCode);
|
||
|
|
Assert.Equal(FollowState.Accepted, (await DB.Default.Find<Following>().OneAsync(following.ID, token)).State);
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task An_accept_from_someone_else_changes_nothing_and_a_reject_ends_the_request()
|
||
|
|
{
|
||
|
|
var token = TestContext.Current.CancellationToken;
|
||
|
|
var (root, alice) = await _harness.Persona("alice");
|
||
|
|
var carol = new RemoteActor(_harness.Peer, "carol");
|
||
|
|
var mallory = new RemoteActor(_harness.Peer, "mallory");
|
||
|
|
await _harness.Follows.Follow(root, new FollowForm { AvatarId = alice.Id, Target = carol.Id }, token);
|
||
|
|
var following = await DB.Default.Find<Following>().Match(f => f.AvatarId == alice.Id).ExecuteSingleAsync(token);
|
||
|
|
|
||
|
|
await _harness.Deliver(mallory, "/human-centipede", new JsonObject
|
||
|
|
{
|
||
|
|
["id"] = $"{mallory.Id}#accepts/{Guid.NewGuid():N}", ["type"] = "Accept", ["actor"] = mallory.Id, ["object"] = following.FollowActivityURI
|
||
|
|
});
|
||
|
|
Assert.Equal(FollowState.Requested, (await DB.Default.Find<Following>().OneAsync(following.ID, token)).State);
|
||
|
|
|
||
|
|
await _harness.Deliver(carol, "/human-centipede", new JsonObject
|
||
|
|
{
|
||
|
|
["id"] = $"{carol.Id}#rejects/{Guid.NewGuid():N}", ["type"] = "Reject", ["actor"] = carol.Id, ["object"] = following.FollowActivityURI
|
||
|
|
});
|
||
|
|
Assert.Null(await DB.Default.Find<Following>().OneAsync(following.ID, token));
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task Unfollowing_a_remote_account_sends_an_undo()
|
||
|
|
{
|
||
|
|
var token = TestContext.Current.CancellationToken;
|
||
|
|
var (root, alice) = await _harness.Persona("alice");
|
||
|
|
var carol = new RemoteActor(_harness.Peer, "carol");
|
||
|
|
await _harness.Follows.Follow(root, new FollowForm { AvatarId = alice.Id, Target = carol.Id }, token);
|
||
|
|
|
||
|
|
await _harness.Follows.Unfollow(root, new FollowForm { AvatarId = alice.Id, Target = carol.Id }, token);
|
||
|
|
|
||
|
|
Assert.False(await DB.Default.Find<Following>().Match(f => f.AvatarId == alice.Id).ExecuteAnyAsync(token));
|
||
|
|
var undo = (await _harness.Outgoing(carol.Id + "/inbox")).Single(a => a["type"]!.GetValue<string>() == "Undo");
|
||
|
|
Assert.Equal("Follow", undo["object"]!["type"]!.GetValue<string>());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|