using GmRelay.Bot.Infrastructure.Telegram; using GmRelay.Shared.Domain; using GmRelay.Shared.Rendering; using Telegram.Bot.Types.ReplyMarkups; namespace GmRelay.Bot.Tests.Rendering; public sealed class TelegramSessionBatchRendererTests { [Fact] public void Render_ShouldProduceCorrectHtmlAndButtons() { 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 (text, markup) = TelegramSessionBatchRenderer.Render(view); Assert.Contains("Campaign", text); Assert.Contains("@alice", text); Assert.Contains("Charlie", text); Assert.Contains("Bob", text); Assert.Contains("Сессия отменена", text); Assert.Contains("Ссылка на игру", text); Assert.Contains("https://example.com/game1", text); Assert.Contains("https://example.com/game2", text); var buttons = markup.InlineKeyboard.SelectMany(row => row).ToList(); Assert.Equal(4, buttons.Count); // 2 sessions x 2 buttons each Assert.Contains(buttons, b => b.CallbackData == $"join_session:{firstSessionId}"); Assert.Contains(buttons, b => b.CallbackData == $"leave_session:{firstSessionId}"); Assert.Contains(buttons, b => b.CallbackData == $"join_session:{secondSessionId}"); Assert.Contains(buttons, b => b.CallbackData == $"leave_session:{secondSessionId}"); Assert.DoesNotContain(buttons, b => b.CallbackData?.StartsWith("cancel") == true); Assert.DoesNotContain(buttons, b => b.CallbackData?.StartsWith("reschedule") == true); } [Fact] public void Render_ShouldSkipButtonsForCancelledSessions() { var cancelledSessionId = Guid.NewGuid(); var sessions = new[] { new SessionBatchDto(cancelledSessionId, DateTime.UtcNow, SessionStatus.Cancelled, null, "") }; var participants = Array.Empty(); var view = SessionBatchViewBuilder.Build("Test", sessions, participants); var (_, markup) = TelegramSessionBatchRenderer.Render(view); Assert.Empty(markup.InlineKeyboard); } [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 (_, markup) = TelegramSessionBatchRenderer.Render(view); var buttons = markup.InlineKeyboard.SelectMany(row => row).ToList(); var joinButton = buttons.First(b => b.CallbackData?.StartsWith("join_session:") == true); Assert.Contains("ожидания", joinButton.Text); } [Fact] public void Render_ShouldHandleEmptySessions() { var view = SessionBatchViewBuilder.Build("Empty", Array.Empty(), Array.Empty()); var (text, markup) = TelegramSessionBatchRenderer.Render(view); Assert.Contains("Empty", text); Assert.DoesNotContain("📅", text); Assert.Empty(markup.InlineKeyboard); } [Fact] public void Render_ShouldEncodeHtmlInTitle() { var sessionId = Guid.NewGuid(); var sessions = new[] { new SessionBatchDto(sessionId, DateTime.UtcNow, SessionStatus.Planned, 4, "") }; var participants = Array.Empty(); var view = SessionBatchViewBuilder.Build("", sessions, participants); var (text, _) = TelegramSessionBatchRenderer.Render(view); Assert.Contains("<script>alert(1)</script>", text); Assert.DoesNotContain("