using MongoDB.Entities; using PrivaPub.Federation.Objects; using PrivaPub.Models.Post; using PrivaPub.Models.Social; using PrivaPub.Tests.Support; using System.Text.Json.Nodes; namespace PrivaPub.Tests.Federation { // Pleroma's and Akkoma's edited posts carry their earlier versions (formerRepresentations): a post first met after its // edits has its history, and an edit tells the ones missed in between [Trait("Category", "Integration")] public sealed class FormerRepresentationsTests : 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(RemoteActor actor) => new Uri(actor.Id).GetLeftPart(UriPartial.Authority); static readonly DateTime Start = DateTime.UtcNow.AddHours(-1); static JsonObject Version(string text, int minutes) => new() { ["type"] = "Note", ["content"] = $"
{text}
", ["published"] = Start.ToString("O"), ["updated"] = minutes == 0 ? default : Start.AddMinutes(minutes).ToString("O") }; async Task<(RemoteActor Carol, JsonObject Note)> Followed(string text, int minutes, params JsonObject[] former) { var token = TestContext.Current.CancellationToken; var (_, alice) = await _harness.Persona("alice"); var carol = new RemoteActor(_harness.Peer, "carol"); await DB.Default.SaveAsync(new Following { AvatarId = alice.Id, TargetActorURI = carol.Id, State = FollowState.Accepted }, token); var note = Version(text, minutes); note["id"] = $"{Origin(carol)}/objects/{Guid.NewGuid():N}"; note["attributedTo"] = carol.Id; note["to"] = new JsonArray(Addressing.Public); note["cc"] = new JsonArray($"{carol.Id}/followers"); if (former.Length > 0) note["formerRepresentations"] = new JsonObject { ["type"] = "OrderedCollection", ["totalItems"] = former.Length, ["orderedItems"] = new JsonArray(former) }; return (carol, note); } Task Deliver(RemoteActor carol, string type, JsonObject note) => _harness.Deliver(carol, "/human-centipede", new JsonObject { ["id"] = $"{Origin(carol)}/activities/{Guid.NewGuid():N}", ["type"] = type, ["actor"] = carol.Id, ["to"] = new JsonArray(Addressing.Public), ["object"] = note.DeepClone() }); Taskfirst
", "second
"], post.Revisions.Select(r => r.ContentHtml)); Assert.Equal("third
", post.ContentHtml); } [Fact] public async Task An_edit_tells_the_versions_missed_in_between() { var (carol, note) = await Followed("first", 0); await Deliver(carol, "Create", note); var edited = (JsonObject)note.DeepClone(); edited["content"] = "third
"; edited["updated"] = Start.AddMinutes(40).ToString("O"); edited["formerRepresentations"] = new JsonObject { ["type"] = "OrderedCollection", ["totalItems"] = 2, ["orderedItems"] = new JsonArray(Version("second", 20), Version("first", 0)) }; await Deliver(carol, "Update", edited); var post = await Stored(note); Assert.Equal(["first
", "second
"], post.Revisions.Select(r => r.ContentHtml)); Assert.Equal("third
", post.ContentHtml); } } }