using TeleWave.Application.Programming.Groups; using TeleWave.Application.Programming.Groups.FindGroupCandidates; 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 FindGroupCandidatesTests { private static (Show Show, MediaAsset Asset) Playable(string name, int minutes) { var asset = MediaAsset.Register($"{name}.mkv", ".mkv", MediaSource.Upload); asset.MarkProcessing(); asset.MarkReady( new MediaReadyInfo( TimeSpan.FromMinutes(minutes), 6, minutes * 10, 1920, 1080, "h264", "aac", $"assets/{name}" ) ); var show = Show.Create(name, ShowKind.Single); show.AddEpisode(asset.Id); return (show, asset); } private static async Task >> FindAsync(TestDb fixture, FindGroupCandidatesQuery query) { await using var db = fixture.New(); return await new FindGroupCandidatesQueryHandler( db, new GroupFilterMatcher(db), new GroupElementResolver(db) ).Handle(query, CancellationToken.None); } [Fact] public async Task MarksWhatIsAlreadyInGroup_AndFiltersByUnitDuration() { var fixture = new TestDb(); var shortFilm = Playable("Короткий", 20); var longFilm = Playable("Длинный", 120); var inside = Playable("Уже внутри", 100); var group = Group.Create("Кино"); group.SetFilter(new GroupFilter(UnitMinutesMin: 60).ToJson()); group.AddElement(GroupElementKind.Show, inside.Show.Id); await using (var seed = fixture.New()) { seed.MediaAssets.AddRange(shortFilm.Asset, longFilm.Asset, inside.Asset); seed.Shows.AddRange(shortFilm.Show, longFilm.Show, inside.Show); seed.Groups.Add(group); await seed.SaveChangesAsync(CancellationToken.None); } var result = await FindAsync(fixture, new FindGroupCandidatesQuery(group.Id)); Assert.True(result.IsSuccess); // Короткий отсечён по длине единицы: правило меряет саму серию, а не сумму по шоу. Assert.Equal(["Длинный", "Уже внутри"], result.Value.Select(c => c.ElementName)); Assert.False(result.Value.Single(c => c.ElementName == "Длинный").AlreadyInGroup); Assert.True(result.Value.Single(c => c.ElementName == "Уже внутри").AlreadyInGroup); } [Fact] public async Task UsesFilterFromQuery_WhenGivenExplicitly() { var fixture = new TestDb(); var film = Playable("Кино", 100); // У группы своего правила нет — редактор крутит фильтр до сохранения. var group = Group.Create("Черновик"); await using (var seed = fixture.New()) { seed.MediaAssets.Add(film.Asset); seed.Shows.Add(film.Show); seed.Groups.Add(group); await seed.SaveChangesAsync(CancellationToken.None); } var withFilter = await FindAsync( fixture, new FindGroupCandidatesQuery(group.Id, new GroupFilter(ShowKinds: [ShowKind.Single])) ); var withoutFilter = await FindAsync(fixture, new FindGroupCandidatesQuery(group.Id)); Assert.Single(withFilter.Value); // Без правила подбирать не из чего — и это отказ, а не пустой список. Assert.False(withoutFilter.IsSuccess); Assert.Equal(GroupErrors.FilterNotSet, withoutFilter.Error); } [Fact] public async Task Fails_WhenGroupIsGone() { var fixture = new TestDb(); var result = await FindAsync(fixture, new FindGroupCandidatesQuery(Guid.NewGuid())); Assert.False(result.IsSuccess); Assert.Equal(GroupErrors.NotFound, result.Error); } }