using Microsoft.EntityFrameworkCore; using NSubstitute; using TeleWave.Application.Broadcast; using TeleWave.Application.Broadcast.Bumpers; using TeleWave.Application.Broadcast.GetChannel; using TeleWave.Application.Broadcast.GetSchedule; using TeleWave.Application.Broadcast.ListChannels; using TeleWave.Application.Broadcast.RemoveChannelAd; using TeleWave.Application.Common.Interfaces; using TeleWave.Application.Library.DeleteShow; using TeleWave.Application.Library.GetShow; using TeleWave.Application.Media.GetMedia; using TeleWave.Application.Tests.Support; using TeleWave.Domain.Broadcast; using TeleWave.Domain.Library; using TeleWave.Domain.Media; using Xunit; namespace TeleWave.Application.Tests.Broadcast; public class QueryHandlersTests { private static readonly DateTimeOffset T0 = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero); [Fact] public async Task GetChannel_ReturnsDto_WithBumperAndOverride() { var fixture = new TestDb(); var channel = Channel.Create("News", "news", T0); var show = Show.Create("S", ShowKind.Series); channel.AddShow(show.Id, 1, BlockMode.Count, 1); channel.AddWeeklyOverride(OverrideMode.Exclusive, 6, 600, 720).AddShow(show.Id, 1); await using (var seed = fixture.New()) { seed.Shows.Add(show); seed.Channels.Add(channel); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var result = await new GetChannelQueryHandler(db).Handle( new GetChannelQuery(channel.Id), CancellationToken.None ); Assert.True(result.IsSuccess); Assert.Equal("News", result.Value.Name); Assert.Single(result.Value.BumperTemplates); Assert.Single(result.Value.Overrides); Assert.Equal(OverrideRecurrence.Weekly, result.Value.Overrides[0].Recurrence); } [Fact] public async Task GetChannel_UnknownId_ReturnsNotFound() { var fixture = new TestDb(); await using var db = fixture.New(); var result = await new GetChannelQueryHandler(db).Handle( new GetChannelQuery(Guid.NewGuid()), CancellationToken.None ); Assert.False(result.IsSuccess); } [Fact] public async Task ListChannels_ReturnsSummaries() { var fixture = new TestDb(); await using (var seed = fixture.New()) { seed.Channels.Add(Channel.Create("A", "a", T0)); seed.Channels.Add(Channel.Create("B", "b", T0)); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var result = await new ListChannelsQueryHandler(db).Handle( new ListChannelsQuery(), CancellationToken.None ); Assert.Equal(2, result.Count); } [Fact] public async Task GetChannelSchedule_ReturnsEntriesWithBumperLabel() { var fixture = new TestDb(); var channel = Channel.Create("c", "c", T0); var show = Show.Create("Show A", ShowKind.Series); var variant = channel.BumperTemplates[0].Variants[0]; var asset = MediaAsset.Register("Show.A.S01E01.mkv", ".mkv", MediaSource.Upload); var program = ScheduleEntry.Program( channel.Id, asset.Id, T0, T0.AddMinutes(20), show.Id, 0 ); var bumper = ScheduleEntry.Bumper( channel.Id, Guid.NewGuid(), T0.AddMinutes(20), T0.AddMinutes(20).AddSeconds(8), show.Id, variant.Id ); await using (var seed = fixture.New()) { seed.Shows.Add(show); seed.Channels.Add(channel); seed.MediaAssets.Add(asset); seed.ScheduleEntries.Add(program); seed.ScheduleEntries.Add(bumper); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var result = await new GetChannelScheduleQueryHandler(db).Handle( new GetChannelScheduleQuery(channel.Id, T0.AddMinutes(-5), T0.AddHours(1)), CancellationToken.None ); Assert.True(result.IsSuccess); Assert.Equal(2, result.Value.Count); var bumperDto = result.Value.Single(e => e.Kind == ScheduleEntryKind.Bumper); Assert.NotNull(bumperDto.BumperName); } [Fact] public async Task GetShow_ReturnsDtoOrNotFound() { var fixture = new TestDb(); var show = Show.Create("A", ShowKind.Series); await using (var seed = fixture.New()) { seed.Shows.Add(show); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var ok = await new GetShowQueryHandler(db).Handle( new GetShowQuery(show.Id), CancellationToken.None ); Assert.True(ok.IsSuccess); var missing = await new GetShowQueryHandler(db).Handle( new GetShowQuery(Guid.NewGuid()), CancellationToken.None ); Assert.False(missing.IsSuccess); } [Fact] public async Task GetMedia_ReturnsDtoOrNotFound() { var fixture = new TestDb(); var asset = MediaAsset.Register("clip.mp4", ".mp4", MediaSource.Upload); await using (var seed = fixture.New()) { seed.MediaAssets.Add(asset); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var ok = await new GetMediaAssetQueryHandler(db).Handle( new GetMediaAssetQuery(asset.Id), CancellationToken.None ); Assert.True(ok.IsSuccess); var missing = await new GetMediaAssetQueryHandler(db).Handle( new GetMediaAssetQuery(Guid.NewGuid()), CancellationToken.None ); Assert.False(missing.IsSuccess); } [Fact] public async Task DeleteShow_RemovesOrNotFound() { var fixture = new TestDb(); var show = Show.Create("A", ShowKind.Series); await using (var seed = fixture.New()) { seed.Shows.Add(show); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var missing = await new DeleteShowCommandHandler(db).Handle( new DeleteShowCommand(Guid.NewGuid()), CancellationToken.None ); Assert.False(missing.IsSuccess); var ok = await new DeleteShowCommandHandler(db).Handle( new DeleteShowCommand(show.Id), CancellationToken.None ); Assert.True(ok.IsSuccess); } [Fact] public async Task RemoveChannelAd_RemovesOrNotFound() { var fixture = new TestDb(); var channel = Channel.Create("c", "c", T0); var ad = channel.AddAd(Guid.NewGuid()); await using (var seed = fixture.New()) { seed.Channels.Add(channel); await seed.SaveChangesAsync(CancellationToken.None); } await using var db = fixture.New(); var handler = new RemoveChannelAdCommandHandler(db); Assert.Equal( ChannelErrors.AdNotFound, ( await handler.Handle( new RemoveChannelAdCommand(channel.Id, Guid.NewGuid()), CancellationToken.None ) ).Error ); Assert.True( ( await handler.Handle( new RemoveChannelAdCommand(channel.Id, ad.Id), CancellationToken.None ) ).IsSuccess ); } [Fact] public async Task AddAndRemoveBumperTemplate_WorkThroughStorage() { var fixture = new TestDb(); var channel = Channel.Create("c", "c", T0); await using (var seed = fixture.New()) { seed.Channels.Add(channel); await seed.SaveChangesAsync(CancellationToken.None); } Guid templateId; await using (var db = fixture.New()) { var added = await new AddBumperTemplateCommandHandler(db).Handle( new AddBumperTemplateCommand(channel.Id, ""), CancellationToken.None ); Assert.True(added.IsSuccess); templateId = added.Value; await db.SaveChangesAsync(CancellationToken.None); } var storage = Substitute.For(); await using (var db = fixture.New()) { var removed = await new RemoveBumperTemplateCommandHandler(db, storage).Handle( new RemoveBumperTemplateCommand(channel.Id, templateId), CancellationToken.None ); Assert.True(removed.IsSuccess); } await storage.Received(1).DeleteTemplateAsync(templateId, Arg.Any()); // дефолтный блок удалить нельзя await using (var db = fixture.New()) { var def = await db .Channels.Include(c => c.BumperTemplates) .FirstAsync(c => c.Id == channel.Id); var result = await new RemoveBumperTemplateCommandHandler(db, storage).Handle( new RemoveBumperTemplateCommand(channel.Id, def.BumperTemplates[0].Id), CancellationToken.None ); Assert.Equal(ChannelErrors.CannotRemoveDefaultBumperTemplate, result.Error); } } }