using TeleWave.Application.Programming.Groups; using TeleWave.Application.Programming.Groups.GetGroup; using TeleWave.Application.Tests.Support; using TeleWave.Domain.Library; using TeleWave.Domain.Media; using TeleWave.Domain.Programming; using Xunit; namespace TeleWave.Application.Tests.Programming; /// /// Карточка группы. Главное свойство — показанное совпадает с тем, что уйдёт в эфир: состав /// считается на месте, а не читается из кэша статистики. /// public class GetGroupTests { [Fact] public async Task StaticGroup_ReturnsItemsWithIdsAndStats() { var fixture = new TestDb(); var asset = ReadyAsset(TimeSpan.FromMinutes(30)); var show = Show.Create("Шоу", ShowKind.Series); show.AddEpisode(asset.Id); var group = Group.Create("Статическая"); group.AddElement(GroupElementKind.Show, show.Id); await SeedAsync(fixture, group, [show], [asset]); var dto = await GetAsync(fixture, group.Id); var item = Assert.Single(dto.Items); // У позиции статической группы есть своя строка — её можно двигать и взвешивать. Assert.NotNull(item.Id); Assert.False(item.Pinned); Assert.Equal("Шоу", item.ElementName); Assert.Equal(1, dto.UnitCount); Assert.Equal(TimeSpan.FromMinutes(30).TotalSeconds, dto.TotalDurationSeconds); Assert.Empty(dto.Excluded); } [Fact] public async Task DynamicGroup_ShowsRuleMatchesWithoutItemId() { var fixture = new TestDb(); var genre = Genre.Create("Мультфильм", "animation"); var byRule = Show.Create("По правилу", ShowKind.Series); byRule.SetGenres([genre.Id]); var pinned = Show.Create("Закреплённое", ShowKind.Single); var group = Group.Create("Динамическая"); group.SetFilter(new GroupFilter(GenreIds: [genre.Id]).ToJson()); group.AddElement(GroupElementKind.Show, pinned.Id); group.SetMode(GroupMode.Dynamic); await using (var seed = fixture.New()) { seed.Genres.Add(genre); seed.Shows.AddRange(byRule, pinned); seed.Groups.Add(group); await seed.SaveChangesAsync(CancellationToken.None); } var dto = await GetAsync(fixture, group.Id); Assert.Equal(GroupMode.Dynamic, dto.Mode); var first = dto.Items[0]; Assert.True(first.Pinned); Assert.NotNull(first.Id); var second = dto.Items[1]; Assert.False(second.Pinned); // Найденному правилом строки в таблице не отвечает — значит, ни веса, ни порядка. Assert.Null(second.Id); } [Fact] public async Task DynamicGroup_ListsExclusionsSeparately() { // Иначе непонятно, почему шоу нет в составе, хотя правило его находит. var fixture = new TestDb(); var genre = Genre.Create("Мультфильм", "animation"); var dropped = Show.Create("Исключённое", ShowKind.Series); dropped.SetGenres([genre.Id]); var group = Group.Create("Динамическая"); group.SetFilter(new GroupFilter(GenreIds: [genre.Id]).ToJson()); group.SetMode(GroupMode.Dynamic); group.Exclude(GroupElementKind.Show, dropped.Id); await using (var seed = fixture.New()) { seed.Genres.Add(genre); seed.Shows.Add(dropped); seed.Groups.Add(group); await seed.SaveChangesAsync(CancellationToken.None); } var dto = await GetAsync(fixture, group.Id); Assert.Empty(dto.Items); var excluded = Assert.Single(dto.Excluded); Assert.Equal("Исключённое", excluded.ElementName); } [Fact] public async Task MissingElement_ShowsDashInsteadOfFailing() { // Элемент мог исчезнуть из библиотеки между чисткой и чтением — экран группы падать не должен. var fixture = new TestDb(); var group = Group.Create("Со следом"); group.AddElement(GroupElementKind.Show, Guid.NewGuid()); await SeedAsync(fixture, group, [], []); var dto = await GetAsync(fixture, group.Id); Assert.Equal("—", Assert.Single(dto.Items).ElementName); } [Fact] public async Task UnknownGroup_ReturnsNotFound() { var fixture = new TestDb(); await using var db = fixture.New(); var result = await Handler(db) .Handle(new GetGroupQuery(Guid.NewGuid()), CancellationToken.None); Assert.False(result.IsSuccess); } private static MediaAsset ReadyAsset(TimeSpan duration) { var asset = MediaAsset.Register($"{Guid.NewGuid():N}.mkv", ".mkv", MediaSource.Upload); asset.MarkProcessing(); asset.MarkReady( new MediaReadyInfo(duration, 2, 900, 1920, 1080, "h264", "aac", "assets/x") ); return asset; } private static async Task SeedAsync( TestDb fixture, Group group, Show[] shows, MediaAsset[] assets ) { await using var seed = fixture.New(); seed.MediaAssets.AddRange(assets); seed.Shows.AddRange(shows); seed.Groups.Add(group); await seed.SaveChangesAsync(CancellationToken.None); } private static GetGroupQueryHandler Handler(Infrastructure.Persistence.AppDbContext db) => new(db, new GroupElementResolver(db), GroupServices.Dynamic(db)); private static async Task GetAsync(TestDb fixture, Guid groupId) { await using var db = fixture.New(); var result = await Handler(db).Handle(new GetGroupQuery(groupId), CancellationToken.None); Assert.True(result.IsSuccess); return result.Value; } }