Add project reference for TeleWave.Infrastructure: include TeleWave.Infrastructure.csproj in TeleWave.Application.Tests project to support additional testing capabilities.
This commit is contained in:
@@ -0,0 +1,324 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TeleWave.Application.Broadcast;
|
||||
using TeleWave.Application.Broadcast.AddChannelAd;
|
||||
using TeleWave.Application.Broadcast.AddChannelShow;
|
||||
using TeleWave.Application.Broadcast.CreateChannel;
|
||||
using TeleWave.Application.Broadcast.CreateOverride;
|
||||
using TeleWave.Application.Broadcast.DeleteOverride;
|
||||
using TeleWave.Application.Broadcast.RemoveChannelShow;
|
||||
using TeleWave.Application.Broadcast.UpdateChannelSettings;
|
||||
using TeleWave.Application.Broadcast.UpdateChannelShow;
|
||||
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 ChannelHandlersTests
|
||||
{
|
||||
private static readonly DateTimeOffset T0 = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
|
||||
|
||||
[Fact]
|
||||
public async Task CreateChannel_Succeeds_AndRejectsDuplicateSlug()
|
||||
{
|
||||
var fixture = new TestDb();
|
||||
await using var db = fixture.New();
|
||||
|
||||
var ok = await new CreateChannelCommandHandler(db).Handle(
|
||||
new CreateChannelCommand("News", "news"),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.True(ok.IsSuccess);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
|
||||
var dup = await new CreateChannelCommandHandler(db).Handle(
|
||||
new CreateChannelCommand("Other", "news"),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.False(dup.IsSuccess);
|
||||
Assert.Equal(ChannelErrors.DuplicateSlug, dup.Error);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddChannelShow_HappyPath_AndErrors()
|
||||
{
|
||||
var fixture = new TestDb();
|
||||
var channel = Channel.Create("c", "c", T0);
|
||||
var show = Show.Create("S", ShowKind.Series);
|
||||
await using (var seed = fixture.New())
|
||||
{
|
||||
seed.Channels.Add(channel);
|
||||
seed.Shows.Add(show);
|
||||
await seed.SaveChangesAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
await using var db = fixture.New();
|
||||
var handler = new AddChannelShowCommandHandler(db);
|
||||
|
||||
// неизвестный канал
|
||||
var noChannel = await handler.Handle(
|
||||
new AddChannelShowCommand(Guid.NewGuid(), show.Id, 1, BlockMode.Count, 1),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.Equal(ChannelErrors.NotFound, noChannel.Error);
|
||||
|
||||
// неизвестное шоу
|
||||
var noShow = await handler.Handle(
|
||||
new AddChannelShowCommand(channel.Id, Guid.NewGuid(), 1, BlockMode.Count, 1),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.Equal(ChannelErrors.ShowNotFound, noShow.Error);
|
||||
|
||||
// ок
|
||||
var ok = await handler.Handle(
|
||||
new AddChannelShowCommand(channel.Id, show.Id, 2, BlockMode.Count, 3),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.True(ok.IsSuccess);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
|
||||
// повторно — уже добавлено
|
||||
await using var db2 = fixture.New();
|
||||
var dup = await new AddChannelShowCommandHandler(db2).Handle(
|
||||
new AddChannelShowCommand(channel.Id, show.Id, 1, BlockMode.Count, 1),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.Equal(ChannelErrors.ShowAlreadyAdded, dup.Error);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RemoveChannelShow_RemovesOrReportsNotFound()
|
||||
{
|
||||
var fixture = new TestDb();
|
||||
var channel = Channel.Create("c", "c", T0);
|
||||
var cs = channel.AddShow(Guid.NewGuid(), 1, BlockMode.Count, 1);
|
||||
await using (var seed = fixture.New())
|
||||
{
|
||||
seed.Channels.Add(channel);
|
||||
await seed.SaveChangesAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
await using var db = fixture.New();
|
||||
var handler = new RemoveChannelShowCommandHandler(db);
|
||||
|
||||
var missing = await handler.Handle(
|
||||
new RemoveChannelShowCommand(channel.Id, Guid.NewGuid()),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.Equal(ChannelErrors.ChannelShowNotFound, missing.Error);
|
||||
|
||||
var ok = await handler.Handle(
|
||||
new RemoveChannelShowCommand(channel.Id, cs.Id),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.True(ok.IsSuccess);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task AddChannelAd_HappyPath_AndAssetNotFound()
|
||||
{
|
||||
var fixture = new TestDb();
|
||||
var channel = Channel.Create("c", "c", T0);
|
||||
var asset = MediaAsset.Register("clip.mp4", ".mp4", MediaSource.Upload);
|
||||
await using (var seed = fixture.New())
|
||||
{
|
||||
seed.Channels.Add(channel);
|
||||
seed.MediaAssets.Add(asset);
|
||||
await seed.SaveChangesAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
await using var db = fixture.New();
|
||||
var handler = new AddChannelAdCommandHandler(db);
|
||||
|
||||
var noAsset = await handler.Handle(
|
||||
new AddChannelAdCommand(channel.Id, Guid.NewGuid()),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.Equal(ChannelErrors.AssetNotFound, noAsset.Error);
|
||||
|
||||
var ok = await handler.Handle(
|
||||
new AddChannelAdCommand(channel.Id, asset.Id),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.True(ok.IsSuccess);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateChannelSettings_UpdatesBumperChances()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
await using var db = fixture.New();
|
||||
var result = await new UpdateChannelSettingsCommandHandler(db).Handle(
|
||||
new UpdateChannelSettingsCommand(
|
||||
channel.Id,
|
||||
"c",
|
||||
true,
|
||||
AdInsertion.BetweenBlocks,
|
||||
1,
|
||||
true,
|
||||
new BumperSettingsInput(
|
||||
BumperFont.Sans,
|
||||
5,
|
||||
BumperSelection.WeightedRandom,
|
||||
0.5,
|
||||
0.2
|
||||
),
|
||||
null
|
||||
),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.True(result.IsSuccess);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
|
||||
await using var verify = fixture.New();
|
||||
var stored = await verify.Channels.FindAsync(channel.Id);
|
||||
Assert.Equal(0.5, stored!.BumperShowChangeChance);
|
||||
Assert.Equal(0.2, stored.BumperEpisodeChangeChance);
|
||||
Assert.Equal(BumperSelection.WeightedRandom, stored.BumperSelection);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateChannelShow_SetsPreferredHours()
|
||||
{
|
||||
var fixture = new TestDb();
|
||||
var channel = Channel.Create("c", "c", T0);
|
||||
var cs = channel.AddShow(Guid.NewGuid(), 1, BlockMode.Count, 1);
|
||||
await using (var seed = fixture.New())
|
||||
{
|
||||
seed.Channels.Add(channel);
|
||||
await seed.SaveChangesAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
await using var db = fixture.New();
|
||||
var result = await new UpdateChannelShowCommandHandler(db).Handle(
|
||||
new UpdateChannelShowCommand(
|
||||
channel.Id,
|
||||
cs.Id,
|
||||
3,
|
||||
BlockMode.Count,
|
||||
2,
|
||||
true,
|
||||
4,
|
||||
[new HourWindowInput(18, 23)]
|
||||
),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.True(result.IsSuccess);
|
||||
await db.SaveChangesAsync(CancellationToken.None);
|
||||
|
||||
await using var verify = fixture.New();
|
||||
var stored = await verify
|
||||
.Channels.Include(c => c.Shows)
|
||||
.ThenInclude(s => s.PreferredHours)
|
||||
.FirstAsync(c => c.Id == channel.Id);
|
||||
var show = Assert.Single(stored.Shows);
|
||||
Assert.Equal(4, show.PreferredWeightMultiplier);
|
||||
Assert.Single(show.PreferredHours);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateOverride_OneTime_Weekly_AndValidation()
|
||||
{
|
||||
var fixture = new TestDb();
|
||||
var channel = Channel.Create("c", "c", T0);
|
||||
var show = Show.Create("S", ShowKind.Series);
|
||||
await using (var seed = fixture.New())
|
||||
{
|
||||
seed.Channels.Add(channel);
|
||||
seed.Shows.Add(show);
|
||||
await seed.SaveChangesAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
await using var db = fixture.New();
|
||||
var handler = new CreateProgrammingOverrideCommandHandler(db);
|
||||
OverrideShowInput[] shows = [new OverrideShowInput(show.Id, 1)];
|
||||
|
||||
// Разовое окно — ок.
|
||||
var oneTime = await handler.Handle(
|
||||
new CreateProgrammingOverrideCommand(
|
||||
channel.Id,
|
||||
OverrideMode.Exclusive,
|
||||
OverrideRecurrence.OneTime,
|
||||
T0,
|
||||
T0.AddHours(2),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
shows
|
||||
),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.True(oneTime.IsSuccess);
|
||||
|
||||
// Еженедельное — ок.
|
||||
var weekly = await handler.Handle(
|
||||
new CreateProgrammingOverrideCommand(
|
||||
channel.Id,
|
||||
OverrideMode.Boost,
|
||||
OverrideRecurrence.Weekly,
|
||||
null,
|
||||
null,
|
||||
6,
|
||||
600,
|
||||
720,
|
||||
shows
|
||||
),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.True(weekly.IsSuccess);
|
||||
|
||||
// Невалидное окно (end <= start).
|
||||
var bad = await handler.Handle(
|
||||
new CreateProgrammingOverrideCommand(
|
||||
channel.Id,
|
||||
OverrideMode.Exclusive,
|
||||
OverrideRecurrence.OneTime,
|
||||
T0.AddHours(2),
|
||||
T0,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
shows
|
||||
),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.Equal(ChannelErrors.InvalidOverrideWindow, bad.Error);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DeleteOverride_RemovesOrReportsNotFound()
|
||||
{
|
||||
var fixture = new TestDb();
|
||||
var channel = Channel.Create("c", "c", T0);
|
||||
var ovr = channel.AddOverride(OverrideMode.Exclusive, T0, T0.AddHours(1));
|
||||
await using (var seed = fixture.New())
|
||||
{
|
||||
seed.Channels.Add(channel);
|
||||
await seed.SaveChangesAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
await using var db = fixture.New();
|
||||
var handler = new DeleteProgrammingOverrideCommandHandler(db);
|
||||
|
||||
var missing = await handler.Handle(
|
||||
new DeleteProgrammingOverrideCommand(channel.Id, Guid.NewGuid()),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.Equal(ChannelErrors.OverrideNotFound, missing.Error);
|
||||
|
||||
var ok = await handler.Handle(
|
||||
new DeleteProgrammingOverrideCommand(channel.Id, ovr.Id),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.True(ok.IsSuccess);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user