109 lines
4.6 KiB
C#
109 lines
4.6 KiB
C#
using MongoDB.Entities;
|
|||
|
|
|
||
|
|
using PrivaPub.Infrastructure.Jobs;
|
||
|
|
using PrivaPub.Models.Jobs;
|
||
|
|
using PrivaPub.Models.Social;
|
||
|
|
using PrivaPub.Tests.Support;
|
||
|
|
|
||
|
|
using System.Text.Json.Nodes;
|
||
|
|
|
||
|
|
using PostEntity = PrivaPub.Models.Post.Post;
|
||
|
|
|
||
|
|
namespace PrivaPub.Tests.Federation
|
||
|
|
{
|
||
|
|
// FEP-171b: a thread's owner adds a reply someone else wrote to the thread's context (Hubzilla's Add{Create}), and the
|
||
|
|
// reply is taken as passed on by the owner, on its own proof; an Add aimed at a collection elsewhere is not a thread's
|
||
|
|
[Trait("Category", "Integration")]
|
||
|
|
public sealed class ConversationContainerTests : IAsyncLifetime
|
||
|
|
{
|
||
|
|
const string Public = "https://www.w3.org/ns/activitystreams#Public";
|
||
|
|
|
||
|
|
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 CancellationToken Token => TestContext.Current.CancellationToken;
|
||
|
|
|
||
|
|
static string Origin(RemoteActor actor) => new Uri(actor.Id).GetLeftPart(UriPartial.Authority);
|
||
|
|
|
||
|
|
static JsonObject Create(RemoteActor author, string text, string context, string inReplyTo = default)
|
||
|
|
{
|
||
|
|
var noteId = $"{Origin(author)}/item/{Guid.NewGuid():N}";
|
||
|
|
var note = new JsonObject
|
||
|
|
{
|
||
|
|
["id"] = noteId, ["type"] = "Note", ["attributedTo"] = author.Id, ["content"] = $"<p>{text}</p>", ["context"] = context,
|
||
|
|
["to"] = new JsonArray(Public), ["published"] = DateTime.UtcNow.ToString("O")
|
||
|
|
};
|
||
|
|
if (inReplyTo != default)
|
||
|
|
note["inReplyTo"] = inReplyTo;
|
||
|
|
return new JsonObject
|
||
|
|
{
|
||
|
|
["id"] = $"{Origin(author)}/activity/{Guid.NewGuid():N}", ["type"] = "Create", ["actor"] = author.Id, ["to"] = new JsonArray(Public),
|
||
|
|
["object"] = note
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
async Task<bool> Held(string objectUri) => await DB.Default.Find<PostEntity>().Match(p => p.ObjectURI == objectUri).ExecuteAnyAsync(Token);
|
||
|
|
|
||
|
|
async Task RunQueued(string innerId)
|
||
|
|
{
|
||
|
|
var job = await DB.Default.Find<Job>().Match(j => j.DedupeKey == "inbox|forwarded|" + innerId && j.State == JobState.Pending).ExecuteFirstAsync(Token);
|
||
|
|
Assert.NotNull(job);
|
||
|
|
Assert.Equal(JobResult.Done, (await _harness.Processor.Handle(job, Token)).Result);
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task A_reply_the_threads_owner_adds_to_its_context_is_taken_on_its_proof()
|
||
|
|
{
|
||
|
|
var (_, alice) = await _harness.Persona("alice");
|
||
|
|
var owner = new RemoteActor(_harness.Peer, "owner", ed25519: true);
|
||
|
|
var commenter = new RemoteActor(_harness.Peer, "commenter", ed25519: true);
|
||
|
|
await DB.Default.SaveAsync(new Following { AvatarId = alice.Id, TargetActorURI = owner.Id, TargetInboxURL = owner.Id + "/inbox", State = FollowState.Accepted }, Token);
|
||
|
|
var context = $"{Origin(owner)}/conversation/{Guid.NewGuid():N}";
|
||
|
|
var root = Create(owner, "a thread", context);
|
||
|
|
await _harness.Deliver(owner, "/human-centipede", root);
|
||
|
|
var rootId = root["object"]!["id"]!.GetValue<string>();
|
||
|
|
Assert.True(await Held(rootId));
|
||
|
|
|
||
|
|
var reply = commenter.Prove(Create(commenter, "a reply in it", context, rootId));
|
||
|
|
var replyId = reply["object"]!["id"]!.GetValue<string>();
|
||
|
|
await _harness.Deliver(owner, "/human-centipede", owner.Prove(new JsonObject
|
||
|
|
{
|
||
|
|
["id"] = $"{Origin(owner)}/activity/{Guid.NewGuid():N}", ["type"] = "Add", ["actor"] = owner.Id, ["object"] = reply,
|
||
|
|
["target"] = new JsonObject { ["id"] = context, ["type"] = "Collection", ["attributedTo"] = owner.Id }, ["to"] = new JsonArray(Public)
|
||
|
|
}));
|
||
|
|
await RunQueued(reply["id"]!.GetValue<string>());
|
||
|
|
|
||
|
|
var stored = await DB.Default.Find<PostEntity>().Match(p => p.ObjectURI == replyId).ExecuteFirstAsync(Token);
|
||
|
|
Assert.NotNull(stored);
|
||
|
|
Assert.Equal(commenter.Id, stored.ActorURI);
|
||
|
|
Assert.Equal((await DB.Default.Find<PostEntity>().Match(p => p.ObjectURI == rootId).ExecuteFirstAsync(Token)).ID, stored.AnsweringToPostId);
|
||
|
|
}
|
||
|
|
|
||
|
|
[Fact]
|
||
|
|
public async Task An_add_to_a_collection_on_another_server_is_no_threads()
|
||
|
|
{
|
||
|
|
var owner = new RemoteActor(_harness.Peer, "owner", ed25519: true);
|
||
|
|
var commenter = new RemoteActor(_harness.Peer, "commenter", _harness.Peer.B, ed25519: true);
|
||
|
|
var reply = commenter.Prove(Create(commenter, "elsewhere", $"{Origin(commenter)}/conversation/1"));
|
||
|
|
|
||
|
|
await _harness.Deliver(owner, "/human-centipede", new JsonObject
|
||
|
|
{
|
||
|
|
["id"] = $"{Origin(owner)}/activity/{Guid.NewGuid():N}", ["type"] = "Add", ["actor"] = owner.Id, ["object"] = reply,
|
||
|
|
["target"] = $"{Origin(commenter)}/conversation/1"
|
||
|
|
});
|
||
|
|
|
||
|
|
Assert.False(await DB.Default.Find<Job>().Match(j => j.DedupeKey == "inbox|forwarded|" + reply["id"]!.GetValue<string>()).ExecuteAnyAsync(Token));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|