feat(guild): update + update_description + update_emblem
This commit is contained in:
@@ -69,4 +69,29 @@ public sealed class GuildRepository : IGuildRepository
|
||||
|
||||
return await q.Take(resultCap).ToListAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateActivityAndJoinConditionAsync(int guildId, int? activity, int? joinCondition, CancellationToken ct = default)
|
||||
{
|
||||
var guild = await _db.Guilds.FirstOrDefaultAsync(g => g.GuildId == guildId, ct);
|
||||
if (guild is null) return;
|
||||
if (activity.HasValue) guild.Activity = (Entities.Guild.GuildActivity)activity.Value;
|
||||
if (joinCondition.HasValue) guild.JoinCondition = (Entities.Guild.GuildJoinCondition)joinCondition.Value;
|
||||
await _db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateDescriptionAsync(int guildId, string description, CancellationToken ct = default)
|
||||
{
|
||||
var guild = await _db.Guilds.FirstOrDefaultAsync(g => g.GuildId == guildId, ct);
|
||||
if (guild is null) return;
|
||||
guild.Description = description;
|
||||
await _db.SaveChangesAsync(ct);
|
||||
}
|
||||
|
||||
public async Task UpdateEmblemAsync(int guildId, long emblemId, CancellationToken ct = default)
|
||||
{
|
||||
var guild = await _db.Guilds.FirstOrDefaultAsync(g => g.GuildId == guildId, ct);
|
||||
if (guild is null) return;
|
||||
guild.EmblemId = emblemId;
|
||||
await _db.SaveChangesAsync(ct);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,4 +24,13 @@ public interface IGuildRepository
|
||||
Task<IReadOnlyList<Entities.Guild.Guild>> SearchAsync(
|
||||
string name, int activity, int joinCondition, int memberBucket,
|
||||
int maxMemberCap, int resultCap, CancellationToken ct = default);
|
||||
|
||||
/// <summary>Updates activity and/or join_condition. Only non-null fields are written.</summary>
|
||||
Task UpdateActivityAndJoinConditionAsync(int guildId, int? activity, int? joinCondition, CancellationToken ct = default);
|
||||
|
||||
/// <summary>Overwrites the description field.</summary>
|
||||
Task UpdateDescriptionAsync(int guildId, string description, CancellationToken ct = default);
|
||||
|
||||
/// <summary>Overwrites the emblem_id field.</summary>
|
||||
Task UpdateEmblemAsync(int guildId, long emblemId, CancellationToken ct = default);
|
||||
}
|
||||
|
||||
@@ -123,14 +123,42 @@ public sealed class GuildService : IGuildService
|
||||
return emblemId is > 0 ? emblemId.Value : 100_000_000L;
|
||||
}
|
||||
|
||||
public Task<GuildOpResult> UpdateAsync(long viewerId, UpdateGuildRequest req, CancellationToken ct = default)
|
||||
=> throw new NotImplementedException();
|
||||
public async Task<GuildOpResult> UpdateAsync(long viewerId, UpdateGuildRequest req, CancellationToken ct = default)
|
||||
{
|
||||
var m = await _members.GetMembershipAsync(viewerId, ct);
|
||||
if (m is null) return new(GuildOpResultCode.NotInGuild);
|
||||
if (m.Role != GuildRole.Leader) return new(GuildOpResultCode.PermissionDenied);
|
||||
|
||||
public Task<GuildOpResult> UpdateDescriptionAsync(long viewerId, string description, CancellationToken ct = default)
|
||||
=> throw new NotImplementedException();
|
||||
if (req.Activity.HasValue && req.Activity.Value is < 1 or > 16)
|
||||
return new(GuildOpResultCode.NameInvalid);
|
||||
if (req.JoinCondition.HasValue && req.JoinCondition.Value is < 1 or > 3)
|
||||
return new(GuildOpResultCode.NameInvalid);
|
||||
|
||||
public Task<GuildOpResult> UpdateEmblemAsync(long viewerId, long emblemId, CancellationToken ct = default)
|
||||
=> throw new NotImplementedException();
|
||||
await _guilds.UpdateActivityAndJoinConditionAsync(m.GuildId, req.Activity, req.JoinCondition, ct);
|
||||
return GuildOpResult.Ok;
|
||||
}
|
||||
|
||||
public async Task<GuildOpResult> UpdateDescriptionAsync(long viewerId, string description, CancellationToken ct = default)
|
||||
{
|
||||
var m = await _members.GetMembershipAsync(viewerId, ct);
|
||||
if (m is null) return new(GuildOpResultCode.NotInGuild);
|
||||
if (m.Role != GuildRole.Leader) return new(GuildOpResultCode.PermissionDenied);
|
||||
if (description.Length > 512) return new(GuildOpResultCode.NameInvalid);
|
||||
|
||||
await _guilds.UpdateDescriptionAsync(m.GuildId, description, ct);
|
||||
await _chat.EmitSystemEventAsync(m.GuildId, viewerId, GuildChatMessageType.Description, body: null, ct);
|
||||
return GuildOpResult.Ok;
|
||||
}
|
||||
|
||||
public async Task<GuildOpResult> UpdateEmblemAsync(long viewerId, long emblemId, CancellationToken ct = default)
|
||||
{
|
||||
var m = await _members.GetMembershipAsync(viewerId, ct);
|
||||
if (m is null) return new(GuildOpResultCode.NotInGuild);
|
||||
if (m.Role != GuildRole.Leader) return new(GuildOpResultCode.PermissionDenied);
|
||||
|
||||
await _guilds.UpdateEmblemAsync(m.GuildId, emblemId, ct);
|
||||
return GuildOpResult.Ok;
|
||||
}
|
||||
|
||||
public async Task<GuildOpResult> BreakupAsync(long viewerId, CancellationToken ct = default)
|
||||
{
|
||||
|
||||
@@ -83,15 +83,52 @@ public sealed class GuildController : SVSimController
|
||||
}
|
||||
|
||||
[HttpPost("update")]
|
||||
public Task<ActionResult<GuildUpdateResponse>> Update([FromBody] GuildUpdateRequest req, CancellationToken ct)
|
||||
=> Task.FromResult<ActionResult<GuildUpdateResponse>>(new GuildUpdateResponse());
|
||||
public async Task<ActionResult<GuildUpdateResponse>> Update([FromBody] GuildUpdateRequest req, CancellationToken ct)
|
||||
{
|
||||
if (!TryGetViewerId(out var viewerId)) return Unauthorized();
|
||||
int? activity = req.Activity != 0 ? req.Activity : null;
|
||||
int? joinCondition = req.JoinCondition != 0 ? req.JoinCondition : null;
|
||||
var r = await _guild.UpdateAsync(viewerId, new(activity, joinCondition), ct);
|
||||
if (!r.IsOk) return WireError();
|
||||
|
||||
var m = await _db.GuildMembers.FirstOrDefaultAsync(m => m.ViewerId == viewerId, ct);
|
||||
if (m is null) return WireError();
|
||||
var guild = await _guild.GetActiveAsync(m.GuildId, ct);
|
||||
if (guild is null) return WireError();
|
||||
var memberCount = await _db.GuildMembers.CountAsync(x => x.GuildId == guild.GuildId, ct);
|
||||
var leader = await _db.Viewers.AsNoTracking().FirstOrDefaultAsync(v => v.Id == guild.LeaderViewerId, ct);
|
||||
return new GuildUpdateResponse
|
||||
{
|
||||
Guild = new GuildDetailSubTree { Detail = ToDetailDto(guild, memberCount, leader?.DisplayName ?? "") }
|
||||
};
|
||||
}
|
||||
|
||||
[HttpPost("update_description")]
|
||||
public Task<ActionResult<EmptyResponse>> UpdateDescription([FromBody] GuildUpdateDescriptionRequest req, CancellationToken ct) => Stub();
|
||||
public async Task<ActionResult<EmptyResponse>> UpdateDescription([FromBody] GuildUpdateDescriptionRequest req, CancellationToken ct)
|
||||
{
|
||||
if (!TryGetViewerId(out var viewerId)) return Unauthorized();
|
||||
var r = await _guild.UpdateDescriptionAsync(viewerId, req.Description, ct);
|
||||
return r.IsOk ? new EmptyResponse() : MapErrorToWire(r);
|
||||
}
|
||||
|
||||
[HttpPost("update_emblem")]
|
||||
public Task<ActionResult<GuildUpdateResponse>> UpdateEmblem([FromBody] GuildUpdateEmblemRequest req, CancellationToken ct)
|
||||
=> Task.FromResult<ActionResult<GuildUpdateResponse>>(new GuildUpdateResponse());
|
||||
public async Task<ActionResult<GuildUpdateResponse>> UpdateEmblem([FromBody] GuildUpdateEmblemRequest req, CancellationToken ct)
|
||||
{
|
||||
if (!TryGetViewerId(out var viewerId)) return Unauthorized();
|
||||
var r = await _guild.UpdateEmblemAsync(viewerId, req.EmblemId, ct);
|
||||
if (!r.IsOk) return WireError();
|
||||
|
||||
var m = await _db.GuildMembers.FirstOrDefaultAsync(m => m.ViewerId == viewerId, ct);
|
||||
if (m is null) return WireError();
|
||||
var guild = await _guild.GetActiveAsync(m.GuildId, ct);
|
||||
if (guild is null) return WireError();
|
||||
var memberCount = await _db.GuildMembers.CountAsync(x => x.GuildId == guild.GuildId, ct);
|
||||
var leader = await _db.Viewers.AsNoTracking().FirstOrDefaultAsync(v => v.Id == guild.LeaderViewerId, ct);
|
||||
return new GuildUpdateResponse
|
||||
{
|
||||
Guild = new GuildDetailSubTree { Detail = ToDetailDto(guild, memberCount, leader?.DisplayName ?? "") }
|
||||
};
|
||||
}
|
||||
|
||||
[HttpPost("search_guild")]
|
||||
public Task<ActionResult<GuildSearchGuildResponse>> SearchGuild([FromBody] GuildSearchGuildRequest req, CancellationToken ct)
|
||||
@@ -238,6 +275,12 @@ public sealed class GuildController : SVSimController
|
||||
private ActionResult<EmptyResponse> MapErrorToWire(GuildOpResult res)
|
||||
=> Ok(new { result_code = GuildErrorResultCode });
|
||||
|
||||
/// <summary>
|
||||
/// Returns a wire-error for endpoints that return a non-EmptyResponse type.
|
||||
/// Identical result_code=2 body, but typed as ObjectResult so it fits any TResult.
|
||||
/// </summary>
|
||||
private ObjectResult WireError() => Ok(new { result_code = GuildErrorResultCode });
|
||||
|
||||
private static Task<ActionResult<EmptyResponse>> Stub() =>
|
||||
Task.FromResult<ActionResult<EmptyResponse>>(new EmptyResponse());
|
||||
}
|
||||
|
||||
366
SVSim.UnitTests/Services/Guild/GuildServiceUpdateTests.cs
Normal file
366
SVSim.UnitTests/Services/Guild/GuildServiceUpdateTests.cs
Normal file
@@ -0,0 +1,366 @@
|
||||
using Microsoft.AspNetCore.TestHost;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Entities.Guild;
|
||||
using SVSim.Database.Services.Guild;
|
||||
using SVSim.UnitTests.Infrastructure;
|
||||
|
||||
namespace SVSim.UnitTests.Services.Guild;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Spy IGuildChatService — counts EmitSystemEventAsync calls per GuildChatMessageType.
|
||||
// ---------------------------------------------------------------------------
|
||||
internal sealed class SpyGuildChatService : IGuildChatService
|
||||
{
|
||||
public readonly List<(int GuildId, long ActorId, GuildChatMessageType Type)> Emissions = new();
|
||||
|
||||
public Task EmitSystemEventAsync(int guildId, long actorViewerId, GuildChatMessageType type, string? body = null, CancellationToken ct = default)
|
||||
{
|
||||
Emissions.Add((guildId, actorViewerId, type));
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
// ---- stub out the rest -----------------------------------------------
|
||||
public Task<ChatWindow> GetWindowAsync(long v, int s, int d, int w, CancellationToken ct = default) => throw new NotImplementedException();
|
||||
public Task<ChatPostResult> PostTextOrStampAsync(long v, int t, string m, CancellationToken ct = default) => throw new NotImplementedException();
|
||||
public Task<ChatPostResult> PostDeckAsync(long v, string j, CancellationToken ct = default) => throw new NotImplementedException();
|
||||
public Task<bool> DeleteDeckAsync(long v, int id, CancellationToken ct = default) => throw new NotImplementedException();
|
||||
public Task<ChatPostResult> PostReplayAsync(long v, string j, CancellationToken ct = default) => throw new NotImplementedException();
|
||||
public Task<string?> GetReplayDetailAsync(long v, int id, CancellationToken ct = default) => throw new NotImplementedException();
|
||||
public Task<string?> GetDeckLogAsync(long v, int id, CancellationToken ct = default) => throw new NotImplementedException();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Factory variant that replaces IGuildChatService with our spy singleton.
|
||||
// ---------------------------------------------------------------------------
|
||||
internal sealed class SpyGuildChatFactory : SVSimTestFactory
|
||||
{
|
||||
public readonly SpyGuildChatService ChatSpy = new();
|
||||
|
||||
protected override void ConfigureWebHost(Microsoft.AspNetCore.Hosting.IWebHostBuilder builder)
|
||||
{
|
||||
base.ConfigureWebHost(builder);
|
||||
builder.ConfigureTestServices(services =>
|
||||
{
|
||||
// Remove all existing registrations for IGuildChatService.
|
||||
var descriptors = services
|
||||
.Where(d => d.ServiceType == typeof(IGuildChatService))
|
||||
.ToList();
|
||||
foreach (var d in descriptors) services.Remove(d);
|
||||
|
||||
// Register our singleton spy so every scope gets the same instance.
|
||||
services.AddSingleton<IGuildChatService>(ChatSpy);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Tests
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
public class GuildServiceUpdateTests
|
||||
{
|
||||
// -----------------------------------------------------------------------
|
||||
// UpdateAsync — activity + join_condition
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
[Test]
|
||||
public async Task UpdateAsync_leader_can_change_activity_and_join_condition()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
var leaderId = await factory.SeedViewerAsync(76_561_198_200_000_001UL, "UpdateLeader1");
|
||||
|
||||
int guildId;
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
var res = await svc.CreateAsync(leaderId, new("UpdateGuild1", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
|
||||
Assert.That(res.IsOk, Is.True);
|
||||
guildId = res.GuildId!.Value;
|
||||
}
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
var r = await svc.UpdateAsync(leaderId, new((int)GuildActivity.Rotation, (int)GuildJoinCondition.Approval));
|
||||
Assert.That(r.IsOk, Is.True);
|
||||
}
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var g = await db.Guilds.FirstAsync(x => x.GuildId == guildId);
|
||||
Assert.That((int)g.Activity, Is.EqualTo((int)GuildActivity.Rotation));
|
||||
Assert.That((int)g.JoinCondition, Is.EqualTo((int)GuildJoinCondition.Approval));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateAsync_non_leader_returns_PermissionDenied()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
var leaderId = await factory.SeedViewerAsync(76_561_198_200_000_002UL, "UpdateLeader2");
|
||||
var memberId = await factory.SeedViewerAsync(76_561_198_200_000_003UL, "UpdateMember2");
|
||||
|
||||
int guildId;
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
var res = await svc.CreateAsync(leaderId, new("UpdateGuild2", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
|
||||
Assert.That(res.IsOk, Is.True);
|
||||
guildId = res.GuildId!.Value;
|
||||
}
|
||||
|
||||
// Add a regular member.
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
db.GuildMembers.Add(new GuildMember { GuildId = guildId, ViewerId = memberId, Role = GuildRole.Regular, JoinedAt = DateTime.UtcNow });
|
||||
var v = await db.Viewers.FirstAsync(x => x.Id == memberId);
|
||||
v.GuildId = guildId;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
var r = await svc.UpdateAsync(memberId, new((int)GuildActivity.Stoic, null));
|
||||
Assert.That(r.Code, Is.EqualTo(GuildOpResultCode.PermissionDenied));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateAsync_invalid_activity_returns_NameInvalid()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
var leaderId = await factory.SeedViewerAsync(76_561_198_200_000_004UL, "UpdateLeader3");
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
await svc.CreateAsync(leaderId, new("UpdateGuild3", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
|
||||
}
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
// Activity 0 is out of range (valid: 1..16).
|
||||
var r = await svc.UpdateAsync(leaderId, new(0, null));
|
||||
Assert.That(r.Code, Is.EqualTo(GuildOpResultCode.NameInvalid));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateAsync_invalid_join_condition_returns_NameInvalid()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
var leaderId = await factory.SeedViewerAsync(76_561_198_200_000_005UL, "UpdateLeader4");
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
await svc.CreateAsync(leaderId, new("UpdateGuild4", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
|
||||
}
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
// JoinCondition 99 is out of range (valid: 1..3).
|
||||
var r = await svc.UpdateAsync(leaderId, new(null, 99));
|
||||
Assert.That(r.Code, Is.EqualTo(GuildOpResultCode.NameInvalid));
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// UpdateDescriptionAsync
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
[Test]
|
||||
public async Task UpdateDescriptionAsync_leader_updates_description_and_emits_chat_event()
|
||||
{
|
||||
using var factory = new SpyGuildChatFactory();
|
||||
var leaderId = await factory.SeedViewerAsync(76_561_198_200_000_010UL, "DescLeader1");
|
||||
|
||||
int guildId;
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
var res = await svc.CreateAsync(leaderId, new("DescGuild1", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
|
||||
Assert.That(res.IsOk, Is.True);
|
||||
guildId = res.GuildId!.Value;
|
||||
}
|
||||
|
||||
// Drain any emissions from CreateAsync (CreateGuild event).
|
||||
factory.ChatSpy.Emissions.Clear();
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
var r = await svc.UpdateDescriptionAsync(leaderId, "New description text");
|
||||
Assert.That(r.IsOk, Is.True);
|
||||
}
|
||||
|
||||
// Verify DB updated.
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var g = await db.Guilds.FirstAsync(x => x.GuildId == guildId);
|
||||
Assert.That(g.Description, Is.EqualTo("New description text"));
|
||||
}
|
||||
|
||||
// Verify exactly one Description chat event was emitted.
|
||||
Assert.That(factory.ChatSpy.Emissions.Count, Is.EqualTo(1));
|
||||
Assert.That(factory.ChatSpy.Emissions[0].Type, Is.EqualTo(GuildChatMessageType.Description));
|
||||
Assert.That(factory.ChatSpy.Emissions[0].GuildId, Is.EqualTo(guildId));
|
||||
Assert.That(factory.ChatSpy.Emissions[0].ActorId, Is.EqualTo(leaderId));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateDescriptionAsync_non_leader_returns_PermissionDenied()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
var leaderId = await factory.SeedViewerAsync(76_561_198_200_000_011UL, "DescLeader2");
|
||||
var memberId = await factory.SeedViewerAsync(76_561_198_200_000_012UL, "DescMember2");
|
||||
|
||||
int guildId;
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
var res = await svc.CreateAsync(leaderId, new("DescGuild2", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
|
||||
Assert.That(res.IsOk, Is.True);
|
||||
guildId = res.GuildId!.Value;
|
||||
}
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
db.GuildMembers.Add(new GuildMember { GuildId = guildId, ViewerId = memberId, Role = GuildRole.Regular, JoinedAt = DateTime.UtcNow });
|
||||
var v = await db.Viewers.FirstAsync(x => x.Id == memberId);
|
||||
v.GuildId = guildId;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
var r = await svc.UpdateDescriptionAsync(memberId, "Hacked");
|
||||
Assert.That(r.Code, Is.EqualTo(GuildOpResultCode.PermissionDenied));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateDescriptionAsync_over_512_chars_returns_NameInvalid()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
var leaderId = await factory.SeedViewerAsync(76_561_198_200_000_013UL, "DescLeader3");
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
await svc.CreateAsync(leaderId, new("DescGuild3", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
|
||||
}
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
var tooLong = new string('x', 513);
|
||||
var r = await svc.UpdateDescriptionAsync(leaderId, tooLong);
|
||||
Assert.That(r.Code, Is.EqualTo(GuildOpResultCode.NameInvalid));
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// UpdateEmblemAsync
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
[Test]
|
||||
public async Task UpdateEmblemAsync_leader_updates_emblem_id()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
var leaderId = await factory.SeedViewerAsync(76_561_198_200_000_020UL, "EmblemLeader1");
|
||||
|
||||
int guildId;
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
var res = await svc.CreateAsync(leaderId, new("EmblemGuild1", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
|
||||
Assert.That(res.IsOk, Is.True);
|
||||
guildId = res.GuildId!.Value;
|
||||
}
|
||||
|
||||
const long newEmblemId = 100_000_042L;
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
var r = await svc.UpdateEmblemAsync(leaderId, newEmblemId);
|
||||
Assert.That(r.IsOk, Is.True);
|
||||
}
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var g = await db.Guilds.FirstAsync(x => x.GuildId == guildId);
|
||||
Assert.That(g.EmblemId, Is.EqualTo(newEmblemId));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateEmblemAsync_non_leader_returns_PermissionDenied()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
var leaderId = await factory.SeedViewerAsync(76_561_198_200_000_021UL, "EmblemLeader2");
|
||||
var memberId = await factory.SeedViewerAsync(76_561_198_200_000_022UL, "EmblemMember2");
|
||||
|
||||
int guildId;
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
var res = await svc.CreateAsync(leaderId, new("EmblemGuild2", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
|
||||
Assert.That(res.IsOk, Is.True);
|
||||
guildId = res.GuildId!.Value;
|
||||
}
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
db.GuildMembers.Add(new GuildMember { GuildId = guildId, ViewerId = memberId, Role = GuildRole.Regular, JoinedAt = DateTime.UtcNow });
|
||||
var v = await db.Viewers.FirstAsync(x => x.Id == memberId);
|
||||
v.GuildId = guildId;
|
||||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
var r = await svc.UpdateEmblemAsync(memberId, 999L);
|
||||
Assert.That(r.Code, Is.EqualTo(GuildOpResultCode.PermissionDenied));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task UpdateEmblemAsync_does_NOT_emit_chat_event()
|
||||
{
|
||||
using var factory = new SpyGuildChatFactory();
|
||||
var leaderId = await factory.SeedViewerAsync(76_561_198_200_000_023UL, "EmblemLeader3");
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
await svc.CreateAsync(leaderId, new("EmblemGuild3", (int)GuildActivity.All, (int)GuildJoinCondition.Free));
|
||||
}
|
||||
|
||||
// Drain CreateGuild system event.
|
||||
factory.ChatSpy.Emissions.Clear();
|
||||
|
||||
using (var scope = factory.Services.CreateScope())
|
||||
{
|
||||
var svc = scope.ServiceProvider.GetRequiredService<IGuildService>();
|
||||
var r = await svc.UpdateEmblemAsync(leaderId, 100_000_099L);
|
||||
Assert.That(r.IsOk, Is.True);
|
||||
}
|
||||
|
||||
Assert.That(factory.ChatSpy.Emissions.Count, Is.EqualTo(0),
|
||||
"UpdateEmblemAsync must NOT emit a chat system event");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user