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