Moves carry follows over; first DMs to Lemmy 0.19 and Mbin go as ChatMessage
Two owner decisions of 2026-10-05, both recorded in ROADMAP: - After a verified Move the personas following the old account follow the new one, in the same lists, and a mute or block of the old account carries over, as Mastodon does it. - A direct message to one account on a server whose NodeInfo names Lemmy before 1.0 or Mbin goes as a ChatMessage, the one place PrivaPub decides by a server's software (invariant 17). G-0008 is closed. Mbin addresses its private messages to the recipient's profile page, so a Create addressed to a persona's /@name now reaches the persona. Checked live: moves 8/8, Lemmy 0.19 30/30, Mbin 26/26 with messages both ways. The software theory runs alone, since every test's peer shares 127.0.0.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
f3e8cce2ed
commit
7b0377c85f
19 files changed
+309
-63
No files matched your search
@@ -0,0 +1,75 @@
|
||||
using MongoDB.Entities;
|
||||
|
||||
using PrivaPub.ClientModels.Post;
|
||||
using PrivaPub.Models.Post;
|
||||
using PrivaPub.Tests.Support;
|
||||
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace PrivaPub.Tests.Federation
|
||||
{
|
||||
// alone in its collection: every test's peer is 127.0.0.1, and the server row it writes decides for whoever writes there
|
||||
[Trait("Category", "Integration")]
|
||||
[Xunit.Collection(nameof(Exclusive))]
|
||||
public sealed class ChatMessageSoftwareTests : 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 NewId(RemoteActor actor, string kind) => $"{new Uri(actor.Id).GetLeftPart(UriPartial.Authority)}/{kind}/{Guid.NewGuid():N}";
|
||||
|
||||
// the one decision taken by a server's software (owner decision 2026-10-05): Lemmy before 1.0 and Mbin get a
|
||||
// ChatMessage even from a persona answering a Note; Lemmy 1.0 a Note
|
||||
[Theory]
|
||||
[InlineData("lemmy", "0.19.20", "ChatMessage")]
|
||||
[InlineData("mbin", "1.10.1", "ChatMessage")]
|
||||
[InlineData("lemmy", "1.0.0-beta.2", "Note")]
|
||||
public async Task A_direct_message_to_lemmy_before_1_0_or_mbin_is_a_chat_message(string software, string version, string sent)
|
||||
{
|
||||
var token = TestContext.Current.CancellationToken;
|
||||
var (_, alice) = await _harness.Persona("alice");
|
||||
var sender = new RemoteActor(_harness.Peer, "pm");
|
||||
var host = new Uri(sender.Id).Host;
|
||||
await DB.Default.DeleteAsync<PrivaPub.Models.Jobs.RemoteInstance>(i => i.Host == host);
|
||||
await DB.Default.SaveAsync(new PrivaPub.Models.Jobs.RemoteInstance { Host = host, Software = software, SoftwareVersion = version }, token);
|
||||
try
|
||||
{
|
||||
var id = NewId(sender, "messages");
|
||||
await _harness.Deliver(sender, "/human-centipede", new JsonObject
|
||||
{
|
||||
["id"] = NewId(sender, "activities/create"), ["type"] = "Create", ["actor"] = sender.Id, ["to"] = new JsonArray(alice.Uri),
|
||||
["object"] = new JsonObject
|
||||
{
|
||||
["id"] = id, ["type"] = "Note", ["attributedTo"] = sender.Id, ["to"] = new JsonArray(alice.Uri),
|
||||
["content"] = "<p>psst</p>", ["published"] = DateTime.UtcNow.ToString("O")
|
||||
}
|
||||
});
|
||||
var message = await DB.Default.Find<Post>().Match(p => p.ObjectURI == id).ExecuteSingleAsync(token);
|
||||
|
||||
var answer = await _harness.Statuses.Publish(alice, new PrivaPub.Domain.Statuses.StatusDraft
|
||||
{
|
||||
Text = "psst back", Visibility = PostVisibility.Direct, ConversationId = message.ConversationId
|
||||
}, token);
|
||||
|
||||
Assert.True(answer.Ok, answer.Error);
|
||||
var create = (await _harness.Outgoing(sender.Id + "/inbox")).Last(a => a["type"]!.GetValue<string>() == "Create");
|
||||
Assert.Equal(sent, create["object"]!["type"]!.GetValue<string>());
|
||||
}
|
||||
finally
|
||||
{
|
||||
await DB.Default.DeleteAsync<PrivaPub.Models.Jobs.RemoteInstance>(i => i.Host == host);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user