1c75994722
- Render SessionBatchViewModel into NetCord EmbedProperties + ActionRowProperties - One embed per session with game title, Moscow date, players, capacity, waitlist, status - Buttons map AvailableAction to ButtonProperties with platform-neutral custom IDs - Cancelled sessions get embed but no action row - Full sessions trigger waitlist button label - 7 tests covering open/full/waitlist/cancelled/reschedule states Closes #27 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
140 lines
6.3 KiB
C#
140 lines
6.3 KiB
C#
using GmRelay.DiscordBot.Rendering;
|
|
using GmRelay.Shared.Domain;
|
|
using GmRelay.Shared.Rendering;
|
|
using NetCord;
|
|
using NetCord.Rest;
|
|
|
|
namespace GmRelay.Bot.Tests.Rendering;
|
|
|
|
public sealed class DiscordSessionBatchRendererTests
|
|
{
|
|
[Fact]
|
|
public void Render_ShouldProduceEmbedsAndButtonsForMultipleSessions()
|
|
{
|
|
var firstSessionId = Guid.NewGuid();
|
|
var secondSessionId = Guid.NewGuid();
|
|
var cancelledSessionId = Guid.NewGuid();
|
|
|
|
var sessions = new[]
|
|
{
|
|
new SessionBatchDto(secondSessionId, new DateTime(2026, 4, 27, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Planned, 4, "https://example.com/game2"),
|
|
new SessionBatchDto(cancelledSessionId, new DateTime(2026, 4, 28, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Cancelled, null, ""),
|
|
new SessionBatchDto(firstSessionId, new DateTime(2026, 4, 26, 18, 0, 0, DateTimeKind.Utc), SessionStatus.Planned, 2, "https://example.com/game1")
|
|
};
|
|
var participants = new[]
|
|
{
|
|
new ParticipantBatchDto(secondSessionId, "Alice", "alice", ParticipantRegistrationStatus.Active),
|
|
new ParticipantBatchDto(secondSessionId, "Charlie", null, ParticipantRegistrationStatus.Waitlisted),
|
|
new ParticipantBatchDto(cancelledSessionId, "Bob", null, ParticipantRegistrationStatus.Active)
|
|
};
|
|
|
|
var view = SessionBatchViewBuilder.Build("Campaign", sessions, participants);
|
|
var (embeds, actionRows) = DiscordSessionBatchRenderer.Render(view);
|
|
|
|
Assert.Equal(3, embeds.Count);
|
|
Assert.Equal(2, actionRows.Count); // cancelled skipped
|
|
|
|
// Embed titles contain game title and Moscow date
|
|
Assert.Contains(embeds, e => e.Title!.Contains("Campaign") && e.Title.Contains("26"));
|
|
Assert.Contains(embeds, e => e.Title!.Contains("Campaign") && e.Title.Contains("27"));
|
|
Assert.Contains(embeds, e => e.Title!.Contains("Campaign") && e.Title.Contains("28"));
|
|
|
|
// Cancelled session embed description indicates cancellation
|
|
var cancelledEmbed = embeds.First(e => e.Description!.Contains("отменена") || e.Description.Contains("Отменена"));
|
|
Assert.NotNull(cancelledEmbed);
|
|
|
|
// Active session embeds contain player names
|
|
Assert.Contains(embeds, e => e.Description!.Contains("Alice"));
|
|
Assert.Contains(embeds, e => e.Description!.Contains("Charlie"));
|
|
|
|
// Buttons for active sessions
|
|
var allButtons = actionRows.SelectMany(r => r).OfType<ButtonProperties>().ToList();
|
|
Assert.Contains(allButtons, b => b.CustomId == $"join_session:{firstSessionId}");
|
|
Assert.Contains(allButtons, b => b.CustomId == $"leave_session:{firstSessionId}");
|
|
Assert.Contains(allButtons, b => b.CustomId == $"join_session:{secondSessionId}");
|
|
Assert.Contains(allButtons, b => b.CustomId == $"leave_session:{secondSessionId}");
|
|
}
|
|
|
|
[Fact]
|
|
public void Render_ShouldSkipActionRowsForCancelledSessions()
|
|
{
|
|
var cancelledSessionId = Guid.NewGuid();
|
|
var sessions = new[] { new SessionBatchDto(cancelledSessionId, DateTime.UtcNow, SessionStatus.Cancelled, null, "") };
|
|
var participants = Array.Empty<ParticipantBatchDto>();
|
|
|
|
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
|
var (embeds, actionRows) = DiscordSessionBatchRenderer.Render(view);
|
|
|
|
Assert.Single(embeds);
|
|
Assert.Empty(actionRows);
|
|
}
|
|
|
|
[Fact]
|
|
public void Render_ShouldShowWaitlistButtonWhenFull()
|
|
{
|
|
var sessionId = Guid.NewGuid();
|
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 1, "https://example.com/game") };
|
|
var participants = new[] { new ParticipantBatchDto(sessionId, "Alice", "alice", ParticipantRegistrationStatus.Active) };
|
|
|
|
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
|
var (_, actionRows) = DiscordSessionBatchRenderer.Render(view);
|
|
var buttons = actionRows.SelectMany(r => r).OfType<ButtonProperties>().ToList();
|
|
var joinButton = buttons.First(b => b.CustomId == $"join_session:{sessionId}");
|
|
|
|
Assert.Contains("ожидания", joinButton.Label);
|
|
}
|
|
|
|
[Fact]
|
|
public void Render_ShouldUseRedColorForCancelledSessions()
|
|
{
|
|
var sessionId = Guid.NewGuid();
|
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Cancelled, null, "") };
|
|
var participants = Array.Empty<ParticipantBatchDto>();
|
|
|
|
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
|
var (embeds, _) = DiscordSessionBatchRenderer.Render(view);
|
|
|
|
Assert.Equal(0xED4245, embeds[0].Color.RawValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void Render_ShouldUseGreenColorForOpenSessions()
|
|
{
|
|
var sessionId = Guid.NewGuid();
|
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "https://example.com/game") };
|
|
var participants = Array.Empty<ParticipantBatchDto>();
|
|
|
|
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
|
var (embeds, _) = DiscordSessionBatchRenderer.Render(view);
|
|
|
|
Assert.Equal(0x57F287, embeds[0].Color.RawValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void Render_ShouldUseYellowColorForFullSessions()
|
|
{
|
|
var sessionId = Guid.NewGuid();
|
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 1, "https://example.com/game") };
|
|
var participants = new[] { new ParticipantBatchDto(sessionId, "Alice", "alice", ParticipantRegistrationStatus.Active) };
|
|
|
|
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
|
var (embeds, _) = DiscordSessionBatchRenderer.Render(view);
|
|
|
|
Assert.Equal(0xFEE75C, embeds[0].Color.RawValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void Render_ShouldHandleRescheduleStatus()
|
|
{
|
|
var sessionId = Guid.NewGuid();
|
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, "Rescheduled", 4, "") };
|
|
var participants = Array.Empty<ParticipantBatchDto>();
|
|
|
|
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
|
var (embeds, actionRows) = DiscordSessionBatchRenderer.Render(view);
|
|
|
|
Assert.Single(embeds);
|
|
Assert.Single(actionRows); // not cancelled → actions present
|
|
}
|
|
}
|