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