using MongoDB.Entities; using PrivaPub.Domain.Statuses; using PrivaPub.Models.Post; using PrivaPub.Tests.Support; using System.Text.Json.Nodes; using static PrivaPub.Tests.Support.FederatedSeeds; namespace PrivaPub.Tests.Federation { // Inbox forwarding of replies (owner decision 2026-10-05): a reply to a persona's post reaches the persona's followers, // who are not among those its author's server sends it to [Trait("Category", "Integration")] public sealed class ReplyRelayTests : 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(); } // alice's post; bob replies from his server, fan follows alice from another, pal from bob's async Task<(PrivaPub.Federation.Actors.LocalActor Alice, Post Post, RemoteActor Bob, RemoteActor Fan, RemoteActor Pal)> Thread() { var token = TestContext.Current.CancellationToken; var (_, alice) = await _harness.Persona("alice"); var bob = new RemoteActor(_harness.Peer, "bob"); var fan = new RemoteActor(_harness.Peer, "fan", _harness.Peer.B); var pal = new RemoteActor(_harness.Peer, "pal"); await _harness.FollowedBy(alice, fan); await _harness.FollowedBy(alice, pal); var post = (await _harness.Statuses.Publish(alice, new StatusDraft { Text = "what do you think?" }, token)).Post; return (alice, post, bob, fan, pal); } JsonObject Reply(RemoteActor bob, Post post, string visibility = "public") { var reply = PublicNote(bob, "
I think so
", post.ActorURI); reply["inReplyTo"] = post.ObjectURI; if (visibility == "followers") { reply["to"] = new JsonArray(bob.Id + "/followers"); reply["cc"] = new JsonArray(post.ActorURI); } return reply; } TaskI think so, on second thought
"; edited["updated"] = DateTime.UtcNow.AddMinutes(1).ToString("O"); var update = Activity(bob, "Update", edited); await _harness.Deliver(bob, "/human-centipede", update); var delete = Activity(bob, "Delete", JsonValue.Create(IdOf(reply))!); await _harness.Deliver(bob, "/human-centipede", delete); var relayed = (await At(fan)).Select(a => a["id"]!.GetValue