Updated the Group and UpdateGroupCommandHandler classes to clarify the behavior of group mode switching. The transition to dynamic mode now clears the existing item list to prevent old items from interfering with the new rule-based composition. Enhanced documentation and comments to explain the implications of mode changes. Updated tests to reflect the new behavior, ensuring that the group correctly handles item lists during mode transitions. Localization strings were also updated to inform users about the changes in item management during mode switching.
168 lines
6.2 KiB
C#
168 lines
6.2 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// Карточка группы. Главное свойство — показанное совпадает с тем, что уйдёт в эфир: состав
|
|
/// считается на месте, а не читается из кэша статистики.
|
|
/// </summary>
|
|
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.SetMode(GroupMode.Dynamic);
|
|
// Закрепляют уже в динамическом режиме: там список — это поправки к правилу.
|
|
group.AddElement(GroupElementKind.Show, pinned.Id);
|
|
|
|
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<GroupDto> 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;
|
|
}
|
|
}
|