Update scheduling parameters and refactor channel endpoints: extend HorizonDays to 7 and RetentionDays to 90 in appsettings.json. Consolidate channel-related endpoint logic by removing obsolete files and enhancing the ShowEndpoints with audience and genre management capabilities. Improve error handling and streamline command handlers for channel operations.
This commit is contained in:
@@ -1,260 +1,203 @@
|
||||
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.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 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<IBumperTemplateStorage>();
|
||||
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<CancellationToken>());
|
||||
|
||||
// дефолтный блок удалить нельзя
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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.Common.Interfaces;
|
||||
using TeleWave.Application.Library.DeleteShow;
|
||||
using TeleWave.Application.Library.GetShow;
|
||||
using TeleWave.Application.Programming.Groups;
|
||||
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);
|
||||
|
||||
private static GroupMembershipCleaner GroupCleaner(IAppDbContext db) =>
|
||||
new(db, new GroupStatsService(db, new GroupElementResolver(db)));
|
||||
|
||||
|
||||
[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 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, GroupCleaner(db)).Handle(
|
||||
new DeleteShowCommand(Guid.NewGuid()),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.False(missing.IsSuccess);
|
||||
|
||||
var ok = await new DeleteShowCommandHandler(db, GroupCleaner(db)).Handle(
|
||||
new DeleteShowCommand(show.Id),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.True(ok.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<IBumperTemplateStorage>();
|
||||
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<CancellationToken>());
|
||||
|
||||
// дефолтный блок удалить нельзя
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user