using TeleWave.Application.Broadcast.Bumpers; using TeleWave.Application.Common.Interfaces; using TeleWave.Domain.Broadcast; using Xunit; namespace TeleWave.Application.Tests.Broadcast; /// /// Подстановка плейсхолдеров. Правило одно и важное: неизвестное значение схлопывается вместе /// с осиротевшим разделителем, а не оставляет дыру в кадре. /// public class BumperPlaceholdersTests { private static readonly DateTimeOffset Moment = new( 2026, 4, 6, 21, 24, 0, TimeSpan.FromHours(3) ); private static BumperContext Full() => new( "Первый", 4, Moment, "Симпсоны", "Терминатор 2", "с5э12", "с1э3", 1991, "Боевик", new TimeOnly(21, 30), "Вечернее кино" ); [Theory] [InlineData("{channel}", "Первый")] [InlineData("{channel.number}", "4")] [InlineData("{now.title}", "Симпсоны")] [InlineData("{next.title}", "Терминатор 2")] [InlineData("{now.episode}", "с5э12")] [InlineData("{next.episode}", "с1э3")] [InlineData("{next.year}", "1991")] [InlineData("{next.genre}", "Боевик")] [InlineData("{next.time}", "21:30")] [InlineData("{time}", "21:24")] [InlineData("{date}", "6 апреля")] [InlineData("{weekday}", "понедельник")] [InlineData("{slot}", "Вечернее кино")] public void Resolve_SubstitutesEveryToken(string text, string expected) => Assert.Equal(expected, BumperPlaceholders.Resolve(text, Full())); [Fact] public void Resolve_MixesTextAndTokens() { var resolved = BumperPlaceholders.Resolve("ДАЛЕЕ В {next.time} — {next.title}", Full()); Assert.Equal("ДАЛЕЕ В 21:30 — Терминатор 2", resolved); } [Fact] public void Resolve_CollapsesGapsFromMissingValues() { var empty = new BumperContext("Канал", null, Moment); // Ни текущей, ни следующей программы: строка писалась ради них и исчезает целиком — // «СЕЙЧАС» в кадре без названия ничего не значит. Assert.Equal("", BumperPlaceholders.Resolve("СЕЙЧАС — {now.title}", empty)); Assert.Equal("", BumperPlaceholders.Resolve("{next.title}", empty)); // А подставилось хоть что-то — строка остаётся, осиротевший хвост подчищается. Assert.Equal("Канал", BumperPlaceholders.Resolve("{channel} {channel.number}", empty)); // Строка, где не подставился ни один плейсхолдер, исчезает целиком: «ДАЛЕЕ В» без времени — // это мусор в кадре, а не подпись. Assert.Equal("", BumperPlaceholders.Resolve("ДАЛЕЕ В {next.time}", empty)); // А постоянный текст без плейсхолдеров остаётся всегда. Assert.Equal("СЕЙЧАС", BumperPlaceholders.Resolve("СЕЙЧАС", empty)); } [Fact] public void Resolve_UnknownTokenLeavesNothing() { // Незнакомый плейсхолдер подставить нечем, а значит и строка пуста — как если бы данных // не нашлось. Сохранить такую строку валидатор всё равно не даст. Assert.Equal("", BumperPlaceholders.Resolve("Далее {next.tittle}", Full())); Assert.Equal("", BumperPlaceholders.Resolve(" ", Full())); // Постоянный текст без плейсхолдеров остаётся как есть. Assert.Equal("Далее", BumperPlaceholders.Resolve("Далее", Full())); } [Fact] public void UnknownTokens_ListsOnlyStrangers() { Assert.Equal( ["next.tittle", "chanel"], BumperPlaceholders.UnknownTokens("{next.title} {next.tittle} {chanel} {next.tittle}") ); Assert.Empty(BumperPlaceholders.UnknownTokens("{channel}")); Assert.Empty(BumperPlaceholders.UnknownTokens(null)); } [Fact] public void TokensIn_CollectsAcrossTexts() { var tokens = BumperPlaceholders.TokensIn(["СЕЙЧАС {now.title}", "", "{next.genre}"]); Assert.Equal(["next.genre", "now.title"], tokens.Order()); } [Theory] [InlineData("{time}", true)] [InlineData("{date}", true)] [InlineData("{weekday}", true)] [InlineData("{next.time}", false)] [InlineData("СЕЙЧАС", false)] [InlineData(null, false)] public void IsVolatile_MarksTokensTiedToTheMoment(string? text, bool expected) => Assert.Equal(expected, BumperPlaceholders.IsVolatile(text)); [Theory] [InlineData(5, 12, "с5э12")] [InlineData(null, 7, "э7")] [InlineData(3, null, null)] [InlineData(null, null, null)] public void Episode_FormatsWhatIsKnown(int? season, int? episode, string? expected) => Assert.Equal(expected, BumperPlaceholders.Episode(season, episode)); [Fact] public void RenderedText_RoundTripsAndSurvivesGarbage() { var lines = new List { new(BumperLineStyle.Label, BumperLineColor.Accent, "СЕЙЧАС"), new(BumperLineStyle.Title, BumperLineColor.Text, "Симпсоны"), }; var restored = BumperRenderedText.FromJson(BumperRenderedText.ToJson(lines)); Assert.Equal(lines, restored); Assert.Empty(BumperRenderedText.FromJson("{не json}")); Assert.Empty(BumperRenderedText.FromJson(null)); } }