fix(guild): subleaders can accept/reject join requests (matches GuildManager.HasAuthorityInviteAndApproval)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Entities.Guild;
|
||||
using SVSim.UnitTests.Infrastructure;
|
||||
|
||||
namespace SVSim.UnitTests.Integration.Guild;
|
||||
@@ -550,7 +552,7 @@ public class GuildJoinFlowTests
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Accept_non_leader_returns_error()
|
||||
public async Task Accept_caller_not_in_any_guild_returns_error()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
|
||||
@@ -593,6 +595,154 @@ public class GuildJoinFlowTests
|
||||
Assert.That(rc.GetInt32(), Is.EqualTo(2), $"Non-guild-member accept must return error, got: {acceptJson}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SubLeader_can_accept_join_request()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
|
||||
// A creates an APPROVAL guild (becomes Leader).
|
||||
long viewerA = await factory.SeedViewerAsync(steamId: 76_561_198_400_001_001UL, displayName: "SubLeaderAcceptLeaderA");
|
||||
using var clientA = factory.CreateAuthenticatedClient(viewerA);
|
||||
int guildId = await CreateGuildAndGetIdAsync(clientA, "SubLeaderAcceptGuild", joinCondition: 2);
|
||||
|
||||
// B joins A's guild as Regular, then we promote to SubLeader directly in the DB.
|
||||
long viewerB = await factory.SeedViewerAsync(steamId: 76_561_198_400_001_002UL, displayName: "SubLeaderAcceptB");
|
||||
using var clientB = factory.CreateAuthenticatedClient(viewerB);
|
||||
await clientB.PostAsync("/guild/join",
|
||||
JsonContent.Create(new { guild_id = guildId, from_invite = false,
|
||||
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
|
||||
// A accepts B so B is a full member.
|
||||
await clientA.PostAsync("/guild/join_request_accept",
|
||||
JsonContent.Create(new { request_viewer_id = (int)viewerB,
|
||||
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
|
||||
// Promote B to SubLeader via direct DB mutation.
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var bMember = await db.GuildMembers.FindAsync(guildId, viewerB);
|
||||
bMember!.Role = GuildRole.SubLeader;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// C applies to A's guild.
|
||||
long viewerC = await factory.SeedViewerAsync(steamId: 76_561_198_400_001_003UL, displayName: "SubLeaderAcceptApplicantC");
|
||||
using var clientC = factory.CreateAuthenticatedClient(viewerC);
|
||||
await clientC.PostAsync("/guild/join",
|
||||
JsonContent.Create(new { guild_id = guildId, from_invite = false,
|
||||
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
|
||||
|
||||
// B (SubLeader) accepts C's request.
|
||||
var acceptResp = await clientB.PostAsync("/guild/join_request_accept",
|
||||
JsonContent.Create(new { request_viewer_id = (int)viewerC,
|
||||
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
|
||||
var acceptJson = await acceptResp.Content.ReadAsStringAsync();
|
||||
Assert.That(acceptResp.IsSuccessStatusCode, Is.True, $"SubLeader accept HTTP failed: {acceptJson}");
|
||||
using var acceptDoc = JsonDocument.Parse(acceptJson);
|
||||
if (acceptDoc.RootElement.TryGetProperty("result_code", out var rc))
|
||||
Assert.That(rc.GetInt32(), Is.Not.EqualTo(2), $"SubLeader accept returned error: {acceptJson}");
|
||||
|
||||
// C must now be a member (guild_status=2).
|
||||
var cInfoResp = await clientC.PostAsync("/guild/info",
|
||||
JsonContent.Create(new { viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
|
||||
var cInfoJson = await cInfoResp.Content.ReadAsStringAsync();
|
||||
using var cInfoDoc = JsonDocument.Parse(cInfoJson);
|
||||
Assert.That(GetStringifiedInt(cInfoDoc.RootElement, "guild_status"), Is.EqualTo(2),
|
||||
$"C must be a member (guild_status=2) after SubLeader accept, got: {cInfoJson}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task SubLeader_can_reject_join_request()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
|
||||
// A creates an APPROVAL guild (becomes Leader).
|
||||
long viewerA = await factory.SeedViewerAsync(steamId: 76_561_198_400_001_011UL, displayName: "SubLeaderRejectLeaderA");
|
||||
using var clientA = factory.CreateAuthenticatedClient(viewerA);
|
||||
int guildId = await CreateGuildAndGetIdAsync(clientA, "SubLeaderRejectGuild", joinCondition: 2);
|
||||
|
||||
// B joins and gets accepted, then promoted to SubLeader.
|
||||
long viewerB = await factory.SeedViewerAsync(steamId: 76_561_198_400_001_012UL, displayName: "SubLeaderRejectB");
|
||||
using var clientB = factory.CreateAuthenticatedClient(viewerB);
|
||||
await clientB.PostAsync("/guild/join",
|
||||
JsonContent.Create(new { guild_id = guildId, from_invite = false,
|
||||
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
|
||||
await clientA.PostAsync("/guild/join_request_accept",
|
||||
JsonContent.Create(new { request_viewer_id = (int)viewerB,
|
||||
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var bMember = await db.GuildMembers.FindAsync(guildId, viewerB);
|
||||
bMember!.Role = GuildRole.SubLeader;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// C applies.
|
||||
long viewerC = await factory.SeedViewerAsync(steamId: 76_561_198_400_001_013UL, displayName: "SubLeaderRejectApplicantC");
|
||||
using var clientC = factory.CreateAuthenticatedClient(viewerC);
|
||||
await clientC.PostAsync("/guild/join",
|
||||
JsonContent.Create(new { guild_id = guildId, from_invite = false,
|
||||
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
|
||||
|
||||
// B (SubLeader) rejects C's request.
|
||||
var rejectResp = await clientB.PostAsync("/guild/reject_join_request",
|
||||
JsonContent.Create(new { request_viewer_id = (int)viewerC,
|
||||
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
|
||||
var rejectJson = await rejectResp.Content.ReadAsStringAsync();
|
||||
Assert.That(rejectResp.IsSuccessStatusCode, Is.True, $"SubLeader reject HTTP failed: {rejectJson}");
|
||||
using var rejectDoc = JsonDocument.Parse(rejectJson);
|
||||
if (rejectDoc.RootElement.TryGetProperty("result_code", out var rc))
|
||||
Assert.That(rc.GetInt32(), Is.Not.EqualTo(2), $"SubLeader reject returned error: {rejectJson}");
|
||||
|
||||
// C must NOT be a member (guild_status=0).
|
||||
var cInfoResp = await clientC.PostAsync("/guild/info",
|
||||
JsonContent.Create(new { viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
|
||||
var cInfoJson = await cInfoResp.Content.ReadAsStringAsync();
|
||||
using var cInfoDoc = JsonDocument.Parse(cInfoJson);
|
||||
Assert.That(GetStringifiedInt(cInfoDoc.RootElement, "guild_status"), Is.EqualTo(0),
|
||||
$"C must NOT be a member (guild_status=0) after SubLeader reject, got: {cInfoJson}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Regular_member_cannot_accept_join_request()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
|
||||
// A creates an APPROVAL guild (becomes Leader).
|
||||
long viewerA = await factory.SeedViewerAsync(steamId: 76_561_198_400_001_021UL, displayName: "RegularPermLeaderA");
|
||||
using var clientA = factory.CreateAuthenticatedClient(viewerA);
|
||||
int guildId = await CreateGuildAndGetIdAsync(clientA, "RegularPermGuild", joinCondition: 2);
|
||||
|
||||
// B joins and gets accepted — remains Regular.
|
||||
long viewerB = await factory.SeedViewerAsync(steamId: 76_561_198_400_001_022UL, displayName: "RegularPermMemberB");
|
||||
using var clientB = factory.CreateAuthenticatedClient(viewerB);
|
||||
await clientB.PostAsync("/guild/join",
|
||||
JsonContent.Create(new { guild_id = guildId, from_invite = false,
|
||||
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
|
||||
await clientA.PostAsync("/guild/join_request_accept",
|
||||
JsonContent.Create(new { request_viewer_id = (int)viewerB,
|
||||
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
|
||||
// B's role is Regular (no promotion).
|
||||
|
||||
// C applies.
|
||||
long viewerC = await factory.SeedViewerAsync(steamId: 76_561_198_400_001_023UL, displayName: "RegularPermApplicantC");
|
||||
using var clientC = factory.CreateAuthenticatedClient(viewerC);
|
||||
await clientC.PostAsync("/guild/join",
|
||||
JsonContent.Create(new { guild_id = guildId, from_invite = false,
|
||||
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
|
||||
|
||||
// B (Regular) tries to accept C's request → PermissionDenied.
|
||||
var acceptResp = await clientB.PostAsync("/guild/join_request_accept",
|
||||
JsonContent.Create(new { request_viewer_id = (int)viewerC,
|
||||
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
|
||||
var acceptJson = await acceptResp.Content.ReadAsStringAsync();
|
||||
Assert.That(acceptResp.IsSuccessStatusCode, Is.True, "HTTP must be 200");
|
||||
using var acceptDoc = JsonDocument.Parse(acceptJson);
|
||||
Assert.That(acceptDoc.RootElement.TryGetProperty("result_code", out var rc), Is.True,
|
||||
$"Expected error from Regular-member accept, got: {acceptJson}");
|
||||
Assert.That(rc.GetInt32(), Is.EqualTo(2), $"Regular member accept must return error (result_code=2), got: {acceptJson}");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Accept_full_guild_returns_error()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user