Mbin joins the pasture; a magazine's own threads and locks are taken
Mbin 1.10.1 runs in the pasture (its image, a messenger worker, a RabbitMQ
of its own, its API limits raised), and peers/mbin_token.py gets mbuser's
token through the authorization-code flow. scenarios/mbin.sh: 24 checks and
one known gap, magazines both ways, titled threads, a Note to a magazine as
a microblog post, comments, favourites and upvotes both ways, a moderator's
lock, unlock and removal, the unfollow and statistics.
What it showed:
- Mbin sends a magazine's threads to its subscribers as the author's Create,
the magazine as its audience, never announced. A post whose group is
followed here and lives on the post's own server is now kept as if
announced; the same from another server is not.
- A moderator's lock is a bare Lock (and Undo{Lock}): LockHandler takes it
from the post's own server only.
- Mbin takes private messages only as ChatMessage and its actors say
nothing about it; PrivaPub never decides by a server's software, so this
stays open as G-0008 for the owner.
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
47d6e22988
commit
26346cde30
15 files changed
+493
-8
No files matched your search
@@ -147,6 +147,60 @@ namespace PrivaPub.Tests.Federation
|
||||
Assert.False(await DB.Default.Find<Post>().Match(p => p.ID == post.ID).ExecuteAnyAsync(token));
|
||||
}
|
||||
|
||||
// Mbin sends a magazine's thread to its subscribers as the author's Create, and a moderator's lock as it is, with no
|
||||
// announce: the magazine's server speaks for them, another server does not
|
||||
[Fact]
|
||||
public async Task A_thread_and_its_lock_that_a_followed_groups_own_server_sends_without_announcing_them_are_taken()
|
||||
{
|
||||
var token = TestContext.Current.CancellationToken;
|
||||
var (root, alice) = await _harness.Persona("alice");
|
||||
var magazine = new RemoteActor(_harness.Peer, "books", type: "Group");
|
||||
var poster = new RemoteActor(_harness.Peer, "poster");
|
||||
var stranger = new RemoteActor(_harness.Peer, "stranger", _harness.Peer.B);
|
||||
await _harness.Follows.Follow(root, new FollowForm { AvatarId = alice.Id, Target = magazine.Id }, token);
|
||||
await DB.Default.Update<Following>().Match(f => f.AvatarId == alice.Id).Modify(f => f.State, FollowState.Accepted).ExecuteAsync(token);
|
||||
JsonObject Page(RemoteActor author) => new()
|
||||
{
|
||||
["id"] = $"{Origin(author)}/m/books/t/{Guid.NewGuid():N}", ["type"] = "Page", ["name"] = "a thread",
|
||||
["attributedTo"] = author.Id, ["content"] = "<p>in the magazine</p>",
|
||||
["to"] = new JsonArray(magazine.Id, Addressing.Public), ["cc"] = new JsonArray(author.Id + "/followers"),
|
||||
["audience"] = magazine.Id, ["published"] = DateTime.UtcNow.ToString("O")
|
||||
};
|
||||
JsonObject Create(RemoteActor author, JsonObject page) => new()
|
||||
{
|
||||
["id"] = $"{Origin(author)}/f/object/{Guid.NewGuid():N}", ["type"] = "Create", ["actor"] = author.Id,
|
||||
["to"] = page["to"]!.DeepClone(), ["cc"] = page["cc"]!.DeepClone(), ["object"] = page
|
||||
};
|
||||
JsonObject Lock(RemoteActor moderator, string uri) => new()
|
||||
{
|
||||
["id"] = $"{Origin(moderator)}/f/object/{Guid.NewGuid():N}", ["type"] = "Lock", ["actor"] = moderator.Id, ["object"] = uri
|
||||
};
|
||||
var page = Page(poster);
|
||||
var elsewhere = Page(stranger);
|
||||
var uri = page["id"]!.GetValue<string>();
|
||||
|
||||
await _harness.Deliver(poster, "/human-centipede", Create(poster, page));
|
||||
await _harness.Deliver(stranger, "/human-centipede", Create(stranger, elsewhere));
|
||||
var kept = await DB.Default.Find<Post>().Match(p => p.ObjectURI == uri).ExecuteSingleAsync(token);
|
||||
await _harness.Deliver(stranger, "/human-centipede", Lock(stranger, uri));
|
||||
var lockedByStranger = (await DB.Default.Find<Post>().OneAsync(kept.ID, token)).LockedAt;
|
||||
var moderator = new RemoteActor(_harness.Peer, "moderator");
|
||||
var locking = Lock(moderator, uri);
|
||||
await _harness.Deliver(moderator, "/human-centipede", locking);
|
||||
var locked = (await DB.Default.Find<Post>().OneAsync(kept.ID, token)).LockedAt;
|
||||
await _harness.Deliver(moderator, "/human-centipede", new JsonObject
|
||||
{
|
||||
["id"] = $"{Origin(moderator)}/f/object/{Guid.NewGuid():N}", ["type"] = "Undo", ["actor"] = moderator.Id, ["object"] = locking.DeepClone()
|
||||
});
|
||||
|
||||
Assert.Equal(magazine.Id, kept.AudienceURI);
|
||||
Assert.True(await DB.Default.Find<TimelineEntry>().Match(e => e.PostId == kept.ID && e.AvatarId == alice.Id).ExecuteAnyAsync(token));
|
||||
Assert.False(await DB.Default.Find<Post>().Match(p => p.ObjectURI == elsewhere["id"]!.GetValue<string>()).ExecuteAnyAsync(token));
|
||||
Assert.Null(lockedByStranger);
|
||||
Assert.NotNull(locked);
|
||||
Assert.Null((await DB.Default.Find<Post>().OneAsync(kept.ID, token)).LockedAt);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_fetched_object_does_not_wear_the_signature_of_the_activity_that_caused_the_fetch()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user