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