230 lines
10 KiB
C#
230 lines
10 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
|
|
}
|
|
|
|
[Fact]
|
|
public void Render_ShouldUseBlueColorForConfirmedSessions()
|
|
{
|
|
var sessionId = Guid.NewGuid();
|
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Confirmed, 4, "https://example.com/game") };
|
|
var participants = Array.Empty<ParticipantBatchDto>();
|
|
|
|
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
|
var (embeds, _) = DiscordSessionBatchRenderer.Render(view);
|
|
|
|
Assert.Equal(0x5865F2, embeds[0].Color.RawValue);
|
|
}
|
|
|
|
[Fact]
|
|
public void Render_ShouldShowEmptyPlayerDescription()
|
|
{
|
|
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.Contains("Пока никто не записался", embeds[0].Description);
|
|
}
|
|
|
|
[Fact]
|
|
public void Render_ShouldSetEmbedUrlWhenJoinLinkPresent()
|
|
{
|
|
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("https://example.com/game", embeds[0].Url);
|
|
}
|
|
|
|
[Fact]
|
|
public void Render_ShouldNormalizeBareDomainJoinLinkForEmbedUrl()
|
|
{
|
|
var sessionId = Guid.NewGuid();
|
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "mobaxterm.mobatek.net/game") };
|
|
var participants = Array.Empty<ParticipantBatchDto>();
|
|
|
|
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
|
var (embeds, _) = DiscordSessionBatchRenderer.Render(view);
|
|
|
|
Assert.Equal("https://mobaxterm.mobatek.net/game", embeds[0].Url);
|
|
}
|
|
|
|
[Fact]
|
|
public void Render_ShouldNotSetEmbedUrlWhenJoinLinkIsNotHttpUrl()
|
|
{
|
|
var sessionId = Guid.NewGuid();
|
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "test") };
|
|
var participants = Array.Empty<ParticipantBatchDto>();
|
|
|
|
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
|
var (embeds, _) = DiscordSessionBatchRenderer.Render(view);
|
|
|
|
Assert.Null(embeds[0].Url);
|
|
}
|
|
|
|
[Fact]
|
|
public void Render_ShouldEmbedCorrectFieldValues()
|
|
{
|
|
var sessionId = Guid.NewGuid();
|
|
var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "https://example.com/game") };
|
|
var participants = new[]
|
|
{
|
|
new ParticipantBatchDto(sessionId, "Alice", "alice", ParticipantRegistrationStatus.Active),
|
|
new ParticipantBatchDto(sessionId, "Bob", null, ParticipantRegistrationStatus.Waitlisted)
|
|
};
|
|
|
|
var view = SessionBatchViewBuilder.Build("Test", sessions, participants);
|
|
var (embeds, _) = DiscordSessionBatchRenderer.Render(view);
|
|
var embed = embeds[0];
|
|
var fields = embed.Fields!.ToList();
|
|
|
|
Assert.Equal(3, fields.Count);
|
|
Assert.Equal("👥 Заполненность", fields[0].Name);
|
|
Assert.Equal("1/4", fields[0].Value);
|
|
Assert.Equal("⏳ Лист ожидания", fields[1].Name);
|
|
Assert.Equal("1", fields[1].Value);
|
|
Assert.Equal("📊 Статус", fields[2].Name);
|
|
Assert.Equal("Запланирована", fields[2].Value);
|
|
}
|
|
}
|