using GmRelay.Bot.Features.Sessions.CreateSession; using Telegram.Bot.Types; namespace GmRelay.Bot.Tests.Features.Sessions.CreateSession; public sealed class NewSessionCommandParserTests { [Fact] public void Parse_ShouldExtractTitleLinkAndUpcomingTimes() { var nowUtc = new DateTimeOffset(2026, 4, 23, 12, 0, 0, TimeSpan.Zero); var text = """ /newsession Название: Curse of Strahd Время: 24.04.2026 19:30 Время: 01.05.2026 20:00 Мест: 4 Ссылка: https://example.test/room """; var result = NewSessionCommandParser.Parse(text, nowUtc); Assert.True(result.IsValid); Assert.Equal("Curse of Strahd", result.Title); Assert.Equal("https://example.test/room", result.Link); Assert.Equal(4, result.MaxPlayers); Assert.Equal( [ new DateTimeOffset(2026, 4, 24, 16, 30, 0, TimeSpan.Zero), new DateTimeOffset(2026, 5, 1, 17, 0, 0, TimeSpan.Zero) ], result.ScheduledTimes); Assert.Empty(result.PastTimeInputs); Assert.Empty(result.InvalidTimeInputs); } [Fact] public void Parse_ShouldExtractOptionalImageUrl() { var nowUtc = new DateTimeOffset(2026, 4, 23, 12, 0, 0, TimeSpan.Zero); var text = """ /newsession Название: Curse of Strahd Время: 24.04.2026 19:30 Ссылка: https://example.test/room Картинка: https://example.test/strahd.jpg """; var result = NewSessionCommandParser.Parse(text, nowUtc); Assert.True(result.IsValid); Assert.Equal("https://example.test/strahd.jpg", result.ImageUrl); } [Fact] public void GetBatchImageReference_ShouldPreferAttachedPhotoOverParsedUrl() { var message = new Message { Photo = [ new PhotoSize { FileId = "small-photo", Width = 320, Height = 180, FileSize = 10 }, new PhotoSize { FileId = "large-photo", Width = 1280, Height = 720, FileSize = 20 } ] }; var imageReference = CreateSessionHandler.GetBatchImageReference( message, "https://example.test/cover.jpg"); Assert.Equal("large-photo", imageReference); } [Fact] public void Parse_ShouldExpandRecurringSchedule_WhenRepeatCountAndIntervalProvided() { var nowUtc = new DateTimeOffset(2026, 4, 23, 12, 0, 0, TimeSpan.Zero); var text = """ /newsession Название: Kingmaker Время: 30.04.2026 19:30 Игр: 4 Интервал: 14 Ссылка: https://example.test/kingmaker """; var result = NewSessionCommandParser.Parse(text, nowUtc); Assert.True(result.IsValid); Assert.Equal( [ new DateTimeOffset(2026, 4, 30, 16, 30, 0, TimeSpan.Zero), new DateTimeOffset(2026, 5, 14, 16, 30, 0, TimeSpan.Zero), new DateTimeOffset(2026, 5, 28, 16, 30, 0, TimeSpan.Zero), new DateTimeOffset(2026, 6, 11, 16, 30, 0, TimeSpan.Zero) ], result.ScheduledTimes); } [Fact] public void Parse_ShouldCollectPastAndInvalidTimes() { var nowUtc = new DateTimeOffset(2026, 4, 23, 12, 0, 0, TimeSpan.Zero); var text = """ Название: Delta Green Время: 20.04.2026 19:30 Время: 31.04.2026 19:30 Время: 25.04.2026 18:00 Ссылка: https://example.test/dg """; var result = NewSessionCommandParser.Parse(text, nowUtc); Assert.True(result.IsValid); Assert.Single(result.ScheduledTimes); Assert.Equal(["20.04.2026 19:30"], result.PastTimeInputs); Assert.Equal(["31.04.2026 19:30"], result.InvalidTimeInputs); } [Fact] public void Parse_ShouldBeInvalid_WhenRequiredFieldsMissing() { var text = """ /newsession Название: Blades in the Dark Время: 25.04.2026 19:30 """; var result = NewSessionCommandParser.Parse(text, DateTimeOffset.UtcNow); Assert.False(result.IsValid); Assert.Null(result.Link); } [Fact] public void Parse_ShouldCollectInvalidSeatLimit() { var text = """ /newsession Название: Blades in the Dark Время: 25.04.2026 19:30 Мест: 0 Ссылка: https://example.test/blades """; var result = NewSessionCommandParser.Parse(text, DateTimeOffset.UtcNow); Assert.False(result.IsValid); Assert.Null(result.MaxPlayers); Assert.Equal(["0"], result.InvalidSeatLimitInputs); } }