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), 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) }; 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); 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) }; 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); } }