A group's owner sees its members and requests, declines and removes
/clientapi/group had no way to see who is in a group or asks to join it, so a circle's owner could not answer a request from elsewhere. Now: - GET /clientapi/group/members: the members (local and remote, with their role) and the pending requests, for the group's owner and moderators only; - POST /clientapi/group/reject: declines a request, telling the asker's server with a Reject of its Follow; - POST /clientapi/group/remove: takes a member out (never the owner): a persona here stops following the group, one elsewhere gets a Reject of its Follow, as Mastodon removes a follower. 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
f38ac73615
commit
1265611f9e
6 files changed
+222
-1
No files matched your search
@@ -202,6 +202,51 @@ namespace PrivaPub.Tests.Http
|
||||
Assert.Equal(circle["url"]!.GetValue<string>(), accept["actor"]!.GetValue<string>());
|
||||
}
|
||||
|
||||
// its owner sees who is in the circle and who asks to join, declines one request and takes members out; nobody else
|
||||
// sees them
|
||||
[Fact]
|
||||
public async Task An_owner_sees_the_members_and_requests_declines_one_and_removes_members()
|
||||
{
|
||||
var token = TestContext.Current.CancellationToken;
|
||||
var (owner, circle, member) = await CircleWithRemoteMember();
|
||||
var groupId = circle["id"]!.GetValue<string>();
|
||||
var joiner = await NewPersona("joiner");
|
||||
using (var joining = _host.As(joiner.Root.Jwt))
|
||||
Assert.True((await joining.PostJson("/clientapi/group/join", new { avatarId = joiner.Id, invitationCode = circle["invitationCode"]!.GetValue<string>() })).IsSuccessStatusCode);
|
||||
var asker = new RemoteActor(_peer, "asker");
|
||||
await _host.Follow(asker, _peer.A, circle["userName"]!.GetValue<string>());
|
||||
var since = DateTime.UtcNow.AddSeconds(-1);
|
||||
using var client = _host.As(owner.Root.Jwt);
|
||||
async Task<List<JsonNode>> Members(HttpClient as_, string avatarId)
|
||||
{
|
||||
var response = await as_.GetAsync($"/clientapi/group/members?avatarId={avatarId}&groupId={groupId}", token);
|
||||
return response.IsSuccessStatusCode ? (await response.JsonItems()).ToList() : null;
|
||||
}
|
||||
|
||||
var listed = await Members(client, owner.Id);
|
||||
using var joinerClient = _host.As(joiner.Root.Jwt);
|
||||
var forJoiner = await Members(joinerClient, joiner.Id);
|
||||
var declined = await client.PostJson("/clientapi/group/reject", new { avatarId = owner.Id, groupId, memberActorURI = asker.Id });
|
||||
var removedRemote = await client.PostJson("/clientapi/group/remove", new { avatarId = owner.Id, groupId, memberActorURI = member.Id });
|
||||
var removedLocal = await client.PostJson("/clientapi/group/remove", new { avatarId = owner.Id, groupId, memberActorURI = joiner.ActorUri() });
|
||||
var removedOwner = await client.PostJson("/clientapi/group/remove", new { avatarId = owner.Id, groupId, memberActorURI = owner.ActorUri() });
|
||||
|
||||
Assert.Null(forJoiner);
|
||||
Assert.Equal(4, listed.Count);
|
||||
Assert.Contains(listed, m => m!["actorUri"]!.GetValue<string>() == owner.ActorUri() && m["role"]!.GetValue<string>() == "owner" && m["isLocal"]!.GetValue<bool>());
|
||||
Assert.Contains(listed, m => m!["actorUri"]!.GetValue<string>() == joiner.ActorUri() && !m["isPending"]!.GetValue<bool>());
|
||||
Assert.Contains(listed, m => m!["actorUri"]!.GetValue<string>() == member.Id && !m["isPending"]!.GetValue<bool>() && !m["isLocal"]!.GetValue<bool>());
|
||||
Assert.Contains(listed, m => m!["actorUri"]!.GetValue<string>() == asker.Id && m["isPending"]!.GetValue<bool>());
|
||||
Assert.True(declined.IsSuccessStatusCode);
|
||||
Assert.True(removedRemote.IsSuccessStatusCode);
|
||||
Assert.True(removedLocal.IsSuccessStatusCode);
|
||||
Assert.Equal(HttpStatusCode.BadRequest, removedOwner.StatusCode);
|
||||
Assert.Equal(new[] { owner.Id }, (await Stored(circle)).Members.Select(m => m.AvatarId));
|
||||
Assert.False(await DB.Default.Find<Follower>().Match(f => f.LocalActorId == groupId && f.ActorURI != owner.ActorUri()).ExecuteAnyAsync(token));
|
||||
Assert.Contains(await Jobs.Deliveries(asker.Id + "/inbox", since, token), d => d["type"]!.GetValue<string>() == "Reject");
|
||||
Assert.Contains(await Jobs.Deliveries(member.Id + "/inbox", since, token), d => d["type"]!.GetValue<string>() == "Reject");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_circle_is_never_found()
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user