using TeleWave.Application.Library.Collections.GetCollection; using TeleWave.Application.Library.Collections.ListCollections; using TeleWave.Application.Library.Interstitials.ListInterstitialBlocks; using TeleWave.Application.Library.Interstitials.ListInterstitials; using TeleWave.Application.Tests.Support; using TeleWave.Domain.Library; using TeleWave.Domain.Media; using Xunit; namespace TeleWave.Application.Tests.Library; /// /// Справочные списки библиотеки: коллекции и ролики. Считают они немного, но каждое число на экране /// админа приходит отсюда — сериал внутри франшизы, длительность блока рекламы, статус обработки. /// public class LibraryQueriesTests { /// Шоу с готовыми ассетами: без длительностей в списках нечего показывать. private static (Show Show, List Assets) Playable( string name, ShowKind kind, int episodes, int minutes ) { var show = Show.Create(name, kind); var assets = new List(); for (var i = 0; i < episodes; i++) { var asset = MediaAsset.Register($"{name}-{i}.mkv", ".mkv", MediaSource.Upload); asset.MarkProcessing(); asset.MarkReady( new MediaReadyInfo( TimeSpan.FromMinutes(minutes), 6, minutes * 10, 1920, 1080, "h264", "aac", $"assets/{name}-{i}" ) ); assets.Add(asset); show.AddEpisode(asset.Id); } return (show, assets); } [Fact] public async Task Collections_CountUnitsNotParts() { var fixture = new TestDb(); var series = Playable("Сериал", ShowKind.Series, 10, 25); var film = Playable("Фильм", ShowKind.Single, 1, 100); var collection = Collection.Create("Франшиза", "Описание"); collection.AddShow(film.Show.Id); collection.AddShow(series.Show.Id); await using (var seed = fixture.New()) { seed.MediaAssets.AddRange([.. series.Assets, .. film.Assets]); seed.Shows.AddRange(series.Show, film.Show); seed.Collections.Add(collection); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var list = await new ListCollectionsQueryHandler(db).Handle( new ListCollectionsQuery(), CancellationToken.None ); var summary = Assert.Single(list); Assert.Equal("Франшиза", summary.Name); Assert.Equal(2, summary.ItemCount); // Единиц больше, чем частей: сериал внутри коллекции разворачивается в свои серии. Assert.Equal(11, summary.UnitCount); var detail = await new GetCollectionQueryHandler(db).Handle( new GetCollectionQuery(collection.Id), CancellationToken.None ); Assert.True(detail.IsSuccess); // Порядок частей — заданный, а не алфавитный: франшиза смотрится по порядку выхода. Assert.Equal(["Фильм", "Сериал"], detail.Value.Items.Select(i => i.ShowName)); Assert.Equal(10, detail.Value.Items[1].EpisodeCount); } [Fact] public async Task GetCollection_FailsForUnknownId() { var fixture = new TestDb(); await using var db = fixture.New(); var result = await new GetCollectionQueryHandler(db).Handle( new GetCollectionQuery(Guid.NewGuid()), CancellationToken.None ); Assert.False(result.IsSuccess); } [Fact] public async Task Interstitials_ShowDurationAndStatus() { var fixture = new TestDb(); var ready = Playable("Реклама", ShowKind.Interstitial, 1, 1); // Ролик без медиа: заведён, но файла к нему ещё не приложили. var empty = Show.Create("Пустой ролик", ShowKind.Interstitial); var notAClip = Playable("Кино", ShowKind.Single, 1, 100); await using (var seed = fixture.New()) { seed.MediaAssets.AddRange([.. ready.Assets, .. notAClip.Assets]); seed.Shows.AddRange(ready.Show, empty, notAClip.Show); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var list = await new ListInterstitialsQueryHandler(db).Handle( new ListInterstitialsQuery(), CancellationToken.None ); // В списке роликов только ролики — полнометражка сюда не попадает. Assert.Equal(2, list.Count); var withMedia = list.Single(i => i.Name == "Реклама"); Assert.Equal(MediaAssetStatus.Ready, withMedia.AssetStatus); Assert.Equal(60, withMedia.DurationSeconds); var withoutMedia = list.Single(i => i.Name == "Пустой ролик"); Assert.Null(withoutMedia.MediaAssetId); Assert.Null(withoutMedia.AssetStatus); Assert.Null(withoutMedia.DurationSeconds); } [Fact] public async Task Blocks_AreCollectionsMadeOfClipsOnly() { var fixture = new TestDb(); var first = Playable("Ролик 1", ShowKind.Interstitial, 1, 1); var second = Playable("Ролик 2", ShowKind.Interstitial, 1, 2); var film = Playable("Кино", ShowKind.Single, 1, 100); var block = Collection.Create("Рекламный блок"); block.AddShow(first.Show.Id); block.AddShow(second.Show.Id); // Смешанная коллекция — франшиза, а не блок: она живёт на своём экране. var mixed = Collection.Create("Смешанная"); mixed.AddShow(first.Show.Id); mixed.AddShow(film.Show.Id); // Пустая коллекция блоком тоже не считается: ставить в эфир из неё нечего. var empty = Collection.Create("Пустая"); await using (var seed = fixture.New()) { seed.MediaAssets.AddRange([.. first.Assets, .. second.Assets, .. film.Assets]); seed.Shows.AddRange(first.Show, second.Show, film.Show); seed.Collections.AddRange(block, mixed, empty); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var blocks = await new ListInterstitialBlocksQueryHandler(db).Handle( new ListInterstitialBlocksQuery(), CancellationToken.None ); var only = Assert.Single(blocks); Assert.Equal("Рекламный блок", only.Name); Assert.Equal(2, only.ItemCount); Assert.Equal(180, only.DurationSeconds); } }