using MongoDB.Entities; using PrivaPub.Api.Mastodon.Mappers; using PrivaPub.Models.User; using PrivaPub.Tests.Support; using System.Text.Json.Nodes; using static PrivaPub.Tests.Support.FederatedSeeds; namespace PrivaPub.Tests.Federation { // An account moving to another (Mastodon's Move), believed as Mastodon believes it: sent by the moving account about // itself, and the new account naming it among its alsoKnownAs [Trait("Category", "Integration")] public sealed class MoveTests : 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(); } // the new account, serving a document that names the old one (or not) RemoteActor Target(string name, RemoteActor aliasOf) { var target = new RemoteActor(_harness.Peer, name); var document = target.Document(); if (aliasOf != default) document["alsoKnownAs"] = new JsonArray(aliasOf.Id); _harness.Peer.Serve(new Uri(target.Id).AbsolutePath, document.ToJsonString()); return target; } Task Move(RemoteActor sender, RemoteActor moved, RemoteActor target) => _harness.Deliver(sender, "/human-centipede", new JsonObject { ["id"] = NewId(sender, "moves"), ["type"] = "Move", ["actor"] = sender.Id, ["object"] = moved.Id, ["target"] = target.Id }); static Task Stored(RemoteActor actor) => DB.Default.Find().Match(a => a.ActorURI == actor.Id).ExecuteFirstAsync(TestContext.Current.CancellationToken); [Fact] public async Task A_move_the_new_account_confirms_shows_the_old_account_moved_there() { var token = TestContext.Current.CancellationToken; var (_, alice) = await _harness.Persona("alice"); var old = new RemoteActor(_harness.Peer, "old"); await Follows(alice.Id, old); var target = Target("new", aliasOf: old); await Move(old, old, target); var stored = await Stored(old); Assert.Equal(target.Id, stored.MovedToURL); var account = await new MastodonMapper(_harness.Db, _harness.Local).Account(stored.ID, token); Assert.Equal(target.Name, account.Moved?.Username); Assert.Contains(_harness.Ledger.Of("in"), e => e.Activity == "Move" && e.Reason == "moved"); } [Fact] public async Task A_move_the_new_account_does_not_confirm_or_someone_else_sends_changes_nothing() { var (_, alice) = await _harness.Persona("alice"); var old = new RemoteActor(_harness.Peer, "old"); var stranger = new RemoteActor(_harness.Peer, "stranger"); await Follows(alice.Id, old); var unconfirmed = Target("new", aliasOf: default); var hijack = Target("hijack", aliasOf: stranger); await Move(old, old, unconfirmed); await Move(stranger, old, hijack); Assert.Null((await Stored(old)).MovedToURL); Assert.Equal(new[] { "not-aliased", "not-self" }, _harness.Ledger.Of("in").Where(e => e.Activity == "Move").Select(e => e.Reason)); } } }