using ErsatzTV.Application.MediaCollections; using ErsatzTV.Application.ProgramSchedules; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Scheduling; using MediatR; using NSubstitute; using NUnit.Framework; using Shouldly; namespace ErsatzTV.Tests.Application.ProgramSchedules; [TestFixture] public class GetProgramScheduleItemsWithDurationsHandlerTests { private IMediaCollectionRepository _mediaCollectionRepository = null!; private IMediator _mediator = null!; [SetUp] public void SetUp() { _mediator = Substitute.For(); _mediaCollectionRepository = Substitute.For(); } [Test] public async Task Handle_Should_Estimate_Items_And_Total_From_Collection_Durations() { ProgramScheduleItemViewModel one = MakeOneItem(1, 10); ProgramScheduleItemViewModel multipleCount = MakeMultipleItem(2, 10, MultipleMode.Count, "3"); ProgramScheduleItemViewModel multipleCollectionSize = MakeMultipleItem(3, 20, MultipleMode.CollectionSize, "0"); ProgramScheduleItemViewModel duration = MakeDurationItem(4, TimeSpan.FromMinutes(45)); ProgramScheduleItemViewModel flood = MakeFloodItem(5, 10); ProgramScheduleItemViewModel remote = MakeOneItem(6, 30); _mediator.Send(Arg.Is(q => q.Id == 99), Arg.Any()) .Returns([one, multipleCount, multipleCollectionSize, duration, flood, remote]); _mediaCollectionRepository.GetItems(10) .Returns([MakeMovie(30), MakeMovie(60), MakeMovie(0)]); _mediaCollectionRepository.GetItems(20) .Returns([MakeMovie(10), MakeMovie(20)]); _mediaCollectionRepository.GetItems(30) .Returns([MakeRemoteStreamWithFallbackDuration(20)]); var handler = new GetProgramScheduleItemsWithDurationsHandler(_mediator, _mediaCollectionRepository); ProgramScheduleItemsWithDurationViewModel result = await handler.Handle(new GetProgramScheduleItemsWithDurations(99), CancellationToken.None); result.Items[0].DurationEstimate.ShouldBe(TimeSpan.FromMinutes(45)); result.Items[1].DurationEstimate.ShouldBe(TimeSpan.FromMinutes(135)); result.Items[2].DurationEstimate.ShouldBe(TimeSpan.FromMinutes(30)); result.Items[3].DurationEstimate.ShouldBe(TimeSpan.FromMinutes(45)); result.Items[4].DurationEstimate.ShouldBeNull(); result.Items[5].DurationEstimate.ShouldBe(TimeSpan.FromMinutes(20)); result.TotalDurationEstimate.ShouldBe(TimeSpan.FromMinutes(275)); } [Test] public async Task Handle_Should_Return_Null_Total_When_No_Items_Can_Be_Estimated() { ProgramScheduleItemViewModel one = MakeOneItem(1, 10); ProgramScheduleItemViewModel flood = MakeFloodItem(2, 10); _mediator.Send(Arg.Is(q => q.Id == 99), Arg.Any()) .Returns([one, flood]); _mediaCollectionRepository.GetItems(10) .Returns([MakeMovie(0)]); var handler = new GetProgramScheduleItemsWithDurationsHandler(_mediator, _mediaCollectionRepository); ProgramScheduleItemsWithDurationViewModel result = await handler.Handle(new GetProgramScheduleItemsWithDurations(99), CancellationToken.None); result.Items.ShouldAllBe(item => item.DurationEstimate == null); result.TotalDurationEstimate.ShouldBeNull(); } private static ProgramScheduleItemOneViewModel MakeOneItem(int id, int collectionId) => new( id, id, StartType.Dynamic, null, null, CollectionType.Collection, MakeCollection(collectionId), null, null, null, null, null, null, null, PlaybackOrder.Shuffle, MarathonGroupBy.None, false, false, null, FillWithGroupMode.None, null, GuideMode.Normal, null, null, null, null, null, [], [], null, null, null, null); private static ProgramScheduleItemMultipleViewModel MakeMultipleItem( int id, int collectionId, MultipleMode multipleMode, string count) => new( id, id, StartType.Dynamic, null, null, CollectionType.Collection, MakeCollection(collectionId), null, null, null, null, null, null, null, PlaybackOrder.Shuffle, MarathonGroupBy.None, false, false, null, FillWithGroupMode.None, multipleMode, count, null, GuideMode.Normal, null, null, null, null, null, [], [], null, null, null, null); private static ProgramScheduleItemDurationViewModel MakeDurationItem(int id, TimeSpan playoutDuration) => new( id, id, StartType.Dynamic, null, null, CollectionType.Collection, MakeCollection(10), null, null, null, null, null, null, null, PlaybackOrder.Shuffle, MarathonGroupBy.None, false, false, null, FillWithGroupMode.None, playoutDuration, TailMode.None, 0, null, GuideMode.Normal, null, null, null, null, null, [], [], null, null, null, null); private static ProgramScheduleItemFloodViewModel MakeFloodItem(int id, int collectionId) => new( id, id, StartType.Dynamic, null, null, CollectionType.Collection, MakeCollection(collectionId), null, null, null, null, null, null, null, PlaybackOrder.Shuffle, MarathonGroupBy.None, false, false, null, FillWithGroupMode.None, null, GuideMode.Normal, null, null, null, null, null, [], [], null, null, null, null); private static MediaCollectionViewModel MakeCollection(int id) => new(CollectionType.Collection, id, $"Collection {id}", false, MediaItemState.Normal); private static Movie MakeMovie(int minutes) => new() { MediaVersions = [new MediaVersion { Duration = TimeSpan.FromMinutes(minutes) }] }; private static RemoteStream MakeRemoteStreamWithFallbackDuration(int minutes) => new() { Duration = TimeSpan.FromMinutes(minutes), MediaVersions = [new MediaVersion { Duration = TimeSpan.Zero }] }; }