feat(guild): /guild/create + populated /guild/info

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
gamer147
2026-06-27 12:17:22 -04:00
parent 99019963ca
commit f7f68d03a7
8 changed files with 430 additions and 11 deletions

View File

@@ -0,0 +1,87 @@
using System.Net.Http.Json;
using System.Text.Json;
using SVSim.UnitTests.Infrastructure;
namespace SVSim.UnitTests.Integration.Guild;
public class GuildCreateInfoFlowTests
{
[Test]
public async Task PostingCreate_then_Info_returns_populated_guild()
{
using var factory = new SVSimTestFactory();
long viewerId = await factory.SeedViewerAsync(
steamId: 76_561_198_200_000_001UL,
displayName: "AlphaIntPlayer");
using var client = factory.CreateAuthenticatedClient(viewerId);
// POST /guild/create — mirror the arena integration test style: include the base-request fields
const string Vid = "0"; const int Sid = 0; const string Stk = "";
var create = await client.PostAsync("/guild/create",
System.Net.Http.Json.JsonContent.Create(new { guild_name = "AlphaInt", activity = 1, join_condition = 1,
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
Assert.That(create.IsSuccessStatusCode, Is.True, $"create failed ({create.StatusCode}): {await create.Content.ReadAsStringAsync()}");
// POST /guild/info — expect JOINING state with guild populated
var info = await client.PostAsync("/guild/info",
System.Net.Http.Json.JsonContent.Create(new { viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk }));
var json = await info.Content.ReadAsStringAsync();
Assert.That(info.IsSuccessStatusCode, Is.True, $"info failed: {json}");
// In test mode (no UnityPlayer user-agent) the translation middleware is a no-op;
// the controller's raw DTO is returned directly (no {data_headers, data} envelope).
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
Assert.That(root.GetProperty("guild_status").GetString(), Is.EqualTo("2"),
"guild_status should be JOINING (2)");
Assert.That(root.GetProperty("guild").GetProperty("detail").GetProperty("guild_name").GetString(),
Is.EqualTo("AlphaInt"));
Assert.That(root.GetProperty("guild").GetProperty("members").GetArrayLength(),
Is.EqualTo(1));
}
[Test]
public async Task PostingCreate_twice_returns_error_second_time()
{
using var factory = new SVSimTestFactory();
long viewerId = await factory.SeedViewerAsync(
steamId: 76_561_198_200_000_002UL,
displayName: "DoubleCreatePlayer");
using var client = factory.CreateAuthenticatedClient(viewerId);
const string Vid = "0"; const int Sid = 0; const string Stk = "";
var createBody = new { guild_name = "OnlyOnce", activity = 1, join_condition = 1,
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk };
// First create should succeed
var first = await client.PostAsync("/guild/create",
System.Net.Http.Json.JsonContent.Create(createBody));
Assert.That(first.IsSuccessStatusCode, Is.True, $"First create failed: {await first.Content.ReadAsStringAsync()}");
var firstJson = await first.Content.ReadAsStringAsync();
using var firstDoc = JsonDocument.Parse(firstJson);
// In test mode (no envelope), result_code may be present (failure) or absent (success EmptyResponse).
// Success path returns EmptyResponse ({}), so result_code will not be present.
var firstRoot = firstDoc.RootElement;
if (firstRoot.TryGetProperty("result_code", out var rc1))
{
// If present it must not be 2 (error code).
Assert.That(rc1.GetInt32(), Is.Not.EqualTo(2), "First create must not error");
}
// Second create for the same viewer should return an error envelope (result_code = 2).
var secondBody = new { guild_name = "OnlyOnceAgain", activity = 1, join_condition = 1,
viewer_id = Vid, steam_id = Sid, steam_session_ticket = Stk };
var second = await client.PostAsync("/guild/create",
System.Net.Http.Json.JsonContent.Create(secondBody));
Assert.That(second.IsSuccessStatusCode, Is.True, "HTTP level should still be 200");
var secondJson = await second.Content.ReadAsStringAsync();
using var secondDoc = JsonDocument.Parse(secondJson);
// Error path: the MapErrorToWire helper returns { result_code = 2 }
Assert.That(secondDoc.RootElement.TryGetProperty("result_code", out var rc2), Is.True,
$"Second create should return result_code field, got: {secondJson}");
Assert.That(rc2.GetInt32(), Is.EqualTo(2), "Second create should fail with result_code=2");
}
}

View File

@@ -0,0 +1,115 @@
using Microsoft.Extensions.DependencyInjection;
using SVSim.Database.Entities.Guild;
using SVSim.Database.Services.Guild;
using SVSim.UnitTests.Infrastructure;
using GuildEntity = SVSim.Database.Entities.Guild.Guild;
namespace SVSim.UnitTests.Services.Guild;
public class GuildServiceCreateTests
{
private static async Task<long> CreateViewerAsync(SVSimTestFactory factory, ulong steamId = 76_561_198_100_000_001UL, string name = "GuildTestViewer")
=> await factory.SeedViewerAsync(steamId: steamId, displayName: name);
private static IGuildService GuildService(SVSimTestFactory factory, out IServiceScope scope)
{
scope = factory.Services.CreateScope();
return scope.ServiceProvider.GetRequiredService<IGuildService>();
}
[Test]
public async Task CreateAsync_makes_a_guild_with_caller_as_leader()
{
using var factory = new SVSimTestFactory();
var viewerId = await CreateViewerAsync(factory, 76_561_198_100_000_001UL, "Alpha Leader");
GuildOpResult res;
using (var scope = factory.Services.CreateScope())
{
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
res = await svc.CreateAsync(viewerId, new("Alpha", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
}
Assert.That(res.IsOk, Is.True);
Assert.That(res.GuildId, Is.Not.Null);
GuildFullView? view;
using (var scope = factory.Services.CreateScope())
{
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
view = await svc.GetMyGuildAsync(viewerId);
}
Assert.That(view, Is.Not.Null);
Assert.That(view!.Guild.Name, Is.EqualTo("Alpha"));
Assert.That(view.Guild.LeaderViewerId, Is.EqualTo(viewerId));
Assert.That(view.Members.Single().Role, Is.EqualTo(GuildRole.Leader));
}
[Test]
public async Task CreateAsync_returns_AlreadyInGuild_when_viewer_already_in_a_guild()
{
using var factory = new SVSimTestFactory();
var viewerId = await CreateViewerAsync(factory, 76_561_198_100_000_002UL, "Already In");
using (var scope = factory.Services.CreateScope())
{
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
var first = await svc.CreateAsync(viewerId, new("FirstGuild", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
Assert.That(first.IsOk, Is.True);
}
using (var scope = factory.Services.CreateScope())
{
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
var second = await svc.CreateAsync(viewerId, new("SecondGuild", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
Assert.That(second.Code, Is.EqualTo(GuildOpResultCode.AlreadyInGuild));
}
}
[Test]
public async Task CreateAsync_returns_NameTaken_when_guild_name_already_exists()
{
using var factory = new SVSimTestFactory();
var v1 = await CreateViewerAsync(factory, 76_561_198_100_000_003UL, "Viewer1");
var v2 = await CreateViewerAsync(factory, 76_561_198_100_000_004UL, "Viewer2");
using (var scope = factory.Services.CreateScope())
{
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
var first = await svc.CreateAsync(v1, new("SameName", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
Assert.That(first.IsOk, Is.True);
}
using (var scope = factory.Services.CreateScope())
{
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
var second = await svc.CreateAsync(v2, new("SameName", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
Assert.That(second.Code, Is.EqualTo(GuildOpResultCode.NameTaken));
}
}
[Test]
public async Task CreateAsync_returns_NameInvalid_for_empty_name()
{
using var factory = new SVSimTestFactory();
var viewerId = await CreateViewerAsync(factory, 76_561_198_100_000_005UL);
using var scope = factory.Services.CreateScope();
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
var res = await svc.CreateAsync(viewerId, new("", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
Assert.That(res.Code, Is.EqualTo(GuildOpResultCode.NameInvalid));
}
[Test]
public async Task GetMyGuildAsync_returns_null_for_viewer_not_in_any_guild()
{
using var factory = new SVSimTestFactory();
var viewerId = await CreateViewerAsync(factory, 76_561_198_100_000_006UL);
using var scope = factory.Services.CreateScope();
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
var view = await svc.GetMyGuildAsync(viewerId);
Assert.That(view, Is.Null);
}
}