Implement BumperEndpoints and remove deprecated bumper-related functionality
ci / build-backend (push) Successful in 1m39s
ci / build-frontend (push) Failing after 26s
ci / tests (push) Skipped
ci / sonar (push) Skipped

Added new BumperEndpoints to the API for managing bumper templates and variants, enhancing the channel management capabilities. Removed outdated bumper-related commands and handlers from the application, streamlining the codebase and improving maintainability. Updated ChannelEndpoints to reflect these changes and ensure proper routing for the new endpoints.
This commit is contained in:
Leonid Pershin
2026-07-27 22:01:56 +03:00
parent ee0b4d2d01
commit ba3721eb92
140 changed files with 7326 additions and 3609 deletions
@@ -1,6 +1,5 @@
using Microsoft.Extensions.Options;
using NSubstitute;
using TeleWave.Application.Broadcast;
using TeleWave.Application.Broadcast.Bumpers;
using TeleWave.Application.Common.Interfaces;
using TeleWave.Application.Streaming;
@@ -16,8 +15,8 @@ namespace TeleWave.Application.Tests.Broadcast;
/// <summary>
/// Превью ТВ-заставки: рендерится каждый подблок блока, длительность выравнивается по длине
/// сегмента HLS, а названия «из/в» берутся из групп этого канала — иначе превью показывало бы
/// случайные шоу из библиотеки.
/// сегмента HLS, а плейсхолдеры подставляются образцами выбранного канала — блок общий, но
/// смотреть на него надо глазами конкретного канала.
/// </summary>
public class BumperPreviewTests
{
@@ -38,49 +37,49 @@ public class BumperPreviewTests
Options.Create(new StreamingOptions { SegmentSeconds = 5 })
);
private static BumperTemplate NewTemplate()
{
var template = BumperTemplate.Create("Блок", "Текст 1");
template
.Variants[0]
.SetLines([
BumperLine.Create(0, BumperLineStyle.Label, BumperLineColor.Accent, "СЕЙЧАС"),
BumperLine.Create(1, BumperLineStyle.Title, BumperLineColor.Text, "{now.title}"),
BumperLine.Create(2, BumperLineStyle.Label, BumperLineColor.Accent, "ДАЛЕЕ"),
BumperLine.Create(3, BumperLineStyle.Title, BumperLineColor.Text, "{next.title}"),
]);
return template;
}
private static IReadOnlyList<BumperRenderSpec> Specs(IBumperRenderer renderer) =>
renderer
.ReceivedCalls()
.Select(c => c.GetArguments()[1])
.OfType<BumperRenderSpec>()
.ToList();
[Fact]
public async Task Render_UnknownChannel_ReturnsNotFound()
public async Task Render_UnknownTemplate_ReturnsNotFound()
{
var fixture = new TestDb();
await using var db = fixture.New();
var result = await Handler(db)
.Handle(
new RenderBumperPreviewCommand(Guid.NewGuid(), Guid.NewGuid()),
CancellationToken.None
);
.Handle(new RenderBumperPreviewCommand(Guid.NewGuid(), null), CancellationToken.None);
Assert.Equal(ChannelErrors.NotFound, result.Error);
Assert.Equal(BumperErrors.TemplateNotFound, result.Error);
}
[Fact]
public async Task Render_UnknownTemplate_ReturnsBumperTemplateNotFound()
public async Task Render_DrawsEveryVariant_WithChannelSamplesAndAlignedDuration()
{
var fixture = new TestDb();
var channel = Channel.Create("Первый", "one", 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 Handler(db)
.Handle(
new RenderBumperPreviewCommand(channel.Id, Guid.NewGuid()),
CancellationToken.None
);
Assert.Equal(ChannelErrors.BumperTemplateNotFound, result.Error);
}
[Fact]
public async Task Render_DrawsEveryVariant_WithChannelShowNamesAndAlignedDuration()
{
var fixture = new TestDb();
var channel = Channel.Create("Первый", "one", T0);
var template = channel.BumperTemplates[0];
var template = NewTemplate();
var second = template.AddVariant("Второй подблок");
second.SetLines([
BumperLine.Create(0, BumperLineStyle.Title, BumperLineColor.Text, "{channel}"),
]);
// Шоу канала: слот сетки ссылается на группу, в группе — шоу.
var show = Show.Create("Наше шоу", ShowKind.Series);
@@ -105,6 +104,7 @@ public class BumperPreviewTests
await using (var seed = fixture.New())
{
seed.Channels.Add(channel);
seed.BumperTemplates.Add(template);
seed.Shows.Add(show);
seed.Groups.Add(group);
seed.ScheduleTemplates.Add(grid);
@@ -115,7 +115,7 @@ public class BumperPreviewTests
await using var db = fixture.New();
var result = await Handler(db, renderer)
.Handle(
new RenderBumperPreviewCommand(channel.Id, template.Id),
new RenderBumperPreviewCommand(template.Id, channel.Id),
CancellationToken.None
);
@@ -136,32 +136,28 @@ public class BumperPreviewTests
Arg.Any<CancellationToken>()
);
var specs = renderer
.ReceivedCalls()
.Select(c => c.GetArguments()[1])
.OfType<BumperRenderSpec>()
.ToList();
var specs = Specs(renderer);
// Своего звука у блока нет: 8 секунд по умолчанию, выровненные вверх до сегмента в 5 сек.
Assert.All(specs, s => Assert.Equal(10, s.DurationSeconds));
Assert.All(specs, s => Assert.Equal("Наше шоу", s.NowTitle));
// Второго шоу у канала нет — подставляется заглушка.
Assert.All(specs, s => Assert.Equal("Второе шоу", s.NextTitle));
Assert.All(specs, s => Assert.Null(s.BackgroundFile));
Assert.All(specs, s => Assert.Null(s.PosterFile));
var nowNext = specs.First(s => s.Lines.Count == 4);
Assert.Equal("Наше шоу", nowNext.Lines[1].Text);
var channelLine = specs.First(s => s.Lines.Count == 1);
Assert.Equal("Первый", channelLine.Lines[0].Text);
}
[Fact]
public async Task Render_WithBackgroundImage_ResolvesPathFromRegistry()
{
var fixture = new TestDb();
var channel = Channel.Create("Первый", "one", T0);
var template = channel.BumperTemplates[0];
var template = NewTemplate();
var image = Image.Create(ImageCategory.BumperBackground, ".png", "bg.png");
template.SetBackgroundImage(image.Id);
await using (var seed = fixture.New())
{
seed.Channels.Add(channel);
seed.BumperTemplates.Add(template);
seed.Images.Add(image);
await seed.SaveChangesAsync(CancellationToken.None);
}
@@ -172,19 +168,40 @@ public class BumperPreviewTests
await using var db = fixture.New();
var result = await Handler(db, renderer, imageStore: imageStore)
.Handle(
new RenderBumperPreviewCommand(channel.Id, template.Id),
CancellationToken.None
);
.Handle(new RenderBumperPreviewCommand(template.Id, null), CancellationToken.None);
Assert.True(result.IsSuccess);
var spec = renderer
.ReceivedCalls()
.Select(c => c.GetArguments()[1])
.OfType<BumperRenderSpec>()
.First();
var spec = Specs(renderer).First();
Assert.Equal("/data/images/bg.png", spec.BackgroundFile);
// Шоу у канала нет — обе подписи заглушечные.
Assert.Equal("Первое шоу", spec.NowTitle);
// Канала нет — образцы заглушечные, но кадр всё равно собирается.
Assert.Equal("Первое шоу", spec.Lines[1].Text);
}
[Fact]
public async Task Render_DropsLinesThatResolveToNothing()
{
var fixture = new TestDb();
var template = BumperTemplate.Create("Блок", "Текст 1");
template
.Variants[0]
.SetLines([
BumperLine.Create(0, BumperLineStyle.Label, BumperLineColor.Accent, "{next.genre}"),
BumperLine.Create(1, BumperLineStyle.Title, BumperLineColor.Text, "{next.title}"),
]);
await using (var seed = fixture.New())
{
seed.BumperTemplates.Add(template);
await seed.SaveChangesAsync(CancellationToken.None);
}
var renderer = Substitute.For<IBumperRenderer>();
await using var db = fixture.New();
await Handler(db, renderer)
.Handle(new RenderBumperPreviewCommand(template.Id, null), CancellationToken.None);
// Жанра у образца нет — строка схлопнулась, а не оставила дыру в кадре.
var spec = Specs(renderer).First();
Assert.Single(spec.Lines);
}
}
@@ -1,5 +1,4 @@
using Microsoft.EntityFrameworkCore;
using TeleWave.Application.Broadcast;
using TeleWave.Application.Broadcast.Bumpers;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Broadcast;
@@ -9,23 +8,25 @@ namespace TeleWave.Application.Tests.Broadcast;
public class BumperVariantHandlersTests
{
private static readonly DateTimeOffset T0 = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
private static BumperTemplate NewTemplate() => BumperTemplate.Create("Блок", "Текст 1");
private static BumperLineDto Line(string text) =>
new(BumperLineStyle.Title, BumperLineColor.Text, text);
[Fact]
public async Task AddVariant_AutoNames_WhenBlank()
public async Task AddVariant_StartsFromDefaultPreset()
{
var fixture = new TestDb();
var channel = Channel.Create("c", "c", T0);
var template = channel.BumperTemplates[0];
var template = NewTemplate();
await using (var seed = fixture.New())
{
seed.Channels.Add(channel);
seed.BumperTemplates.Add(template);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var result = await new AddBumperTextVariantCommandHandler(db).Handle(
new AddBumperTextVariantCommand(channel.Id, template.Id, " "),
var result = await new AddBumperVariantCommandHandler(db).Handle(
new AddBumperVariantCommand(template.Id, "Текст 2"),
CancellationToken.None
);
Assert.True(result.IsSuccess);
@@ -33,60 +34,60 @@ public class BumperVariantHandlersTests
await using var verify = fixture.New();
var stored = await verify
.Channels.Include(c => c.BumperTemplates)
.ThenInclude(t => t.Variants)
.FirstAsync(c => c.Id == channel.Id);
var variants = stored.BumperTemplates[0].Variants;
Assert.Equal(2, variants.Count);
Assert.Contains(variants, v => v.Name == "Текст 2");
.BumperTemplates.Include(t => t.Variants)
.FirstAsync(t => t.Id == template.Id);
Assert.Equal(2, stored.Variants.Count);
var added = stored.Variants.First(v => v.Name == "Текст 2");
// Пустой подблок в редакторе выглядит поломанным — новый начинается с «Сейчас/Далее».
Assert.Equal(4, added.Lines.Count);
Assert.Contains(added.Lines, l => l.Text == "{next.title}");
}
[Fact]
public async Task AddVariant_UnknownTemplate_ReturnsNotFound()
{
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 AddBumperTextVariantCommandHandler(db).Handle(
new AddBumperTextVariantCommand(channel.Id, Guid.NewGuid(), "x"),
var result = await new AddBumperVariantCommandHandler(db).Handle(
new AddBumperVariantCommand(Guid.NewGuid(), "x"),
CancellationToken.None
);
Assert.Equal(ChannelErrors.BumperTemplateNotFound, result.Error);
Assert.Equal(BumperErrors.TemplateNotFound, result.Error);
}
[Fact]
public async Task UpdateVariant_ChangesFields()
public async Task UpdateVariant_ReplacesLinesAndFields()
{
var fixture = new TestDb();
var channel = Channel.Create("c", "c", T0);
var template = channel.BumperTemplates[0];
var template = NewTemplate();
var variant = template.Variants[0];
await using (var seed = fixture.New())
{
seed.Channels.Add(channel);
seed.BumperTemplates.Add(template);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var result = await new UpdateBumperTextVariantCommandHandler(db).Handle(
new UpdateBumperTextVariantCommand(
channel.Id,
var result = await new UpdateBumperVariantCommandHandler(db).Handle(
new UpdateBumperVariantCommand(
template.Id,
variant.Id,
"Custom",
BumperTextKind.Free,
"NOW",
"NEXT",
"line1",
"line2",
BumperTrigger.Both,
7
new BumperVariantInput(
"Custom",
BumperTrigger.Both,
BumperBackground.Template,
7,
[
new BumperLineDto(
BumperLineStyle.Label,
BumperLineColor.Accent,
"ДАЛЕЕ В {next.time}"
),
Line("{next.title}"),
]
)
),
CancellationToken.None
);
@@ -95,45 +96,75 @@ public class BumperVariantHandlersTests
await using var verify = fixture.New();
var stored = await verify
.Channels.Include(c => c.BumperTemplates)
.ThenInclude(t => t.Variants)
.FirstAsync(c => c.Id == channel.Id);
var v = stored.BumperTemplates[0].Variants[0];
.BumperTemplates.Include(t => t.Variants)
.FirstAsync(t => t.Id == template.Id);
var v = stored.Variants[0];
Assert.Equal("Custom", v.Name);
Assert.Equal(BumperTextKind.Free, v.Kind);
Assert.Equal(7, v.Weight);
Assert.Equal(BumperTrigger.Both, v.Trigger);
Assert.Equal(BumperBackground.Template, v.Background);
Assert.Equal(7, v.Weight);
Assert.Equal(2, v.Lines.Count);
Assert.Equal("ДАЛЕЕ В {next.time}", v.Lines[0].Text);
}
[Fact]
public async Task UpdateVariant_UnknownPlaceholder_IsRejected()
{
var fixture = new TestDb();
var template = NewTemplate();
var variant = template.Variants[0];
await using (var seed = fixture.New())
{
seed.BumperTemplates.Add(template);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var result = await new UpdateBumperVariantCommandHandler(db).Handle(
new UpdateBumperVariantCommand(
template.Id,
variant.Id,
new BumperVariantInput(
"Custom",
BumperTrigger.Both,
BumperBackground.Template,
1,
[Line("Далее {next.tittle}")]
)
),
CancellationToken.None
);
// В эфире опечатка превратилась бы в пустоту, и заметить это было бы уже некому.
Assert.Equal("Bumpers.UnknownPlaceholders", result.Error.Code);
}
[Fact]
public async Task RemoveVariant_CannotRemoveLast_ButRemovesExtra()
{
var fixture = new TestDb();
var channel = Channel.Create("c", "c", T0);
var template = channel.BumperTemplates[0];
var template = NewTemplate();
var only = template.Variants[0];
var extra = template.AddVariant("Text 2");
var extra = template.AddVariant("Текст 2");
await using (var seed = fixture.New())
{
seed.Channels.Add(channel);
seed.BumperTemplates.Add(template);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var handler = new RemoveBumperTextVariantCommandHandler(db);
var okRemove = await handler.Handle(
new RemoveBumperTextVariantCommand(channel.Id, template.Id, extra.Id),
var okRemove = await new RemoveBumperVariantCommandHandler(db).Handle(
new RemoveBumperVariantCommand(template.Id, extra.Id),
CancellationToken.None
);
Assert.True(okRemove.IsSuccess);
await db.SaveChangesAsync(CancellationToken.None);
await using var db2 = fixture.New();
var lastGuard = await new RemoveBumperTextVariantCommandHandler(db2).Handle(
new RemoveBumperTextVariantCommand(channel.Id, template.Id, only.Id),
var lastGuard = await new RemoveBumperVariantCommandHandler(db2).Handle(
new RemoveBumperVariantCommand(template.Id, only.Id),
CancellationToken.None
);
Assert.Equal(ChannelErrors.CannotRemoveLastBumperTextVariant, lastGuard.Error);
Assert.Equal(BumperErrors.CannotRemoveLastVariant, lastGuard.Error);
}
}
@@ -7,26 +7,23 @@ using Xunit;
namespace TeleWave.Application.Tests.Broadcast;
/// <summary>
/// Карточка канала: собственные свойства канала плюс блоки заставки с подблоками. Сетка сюда не
/// входит — она запрашивается отдельно.
/// Карточка канала: только собственные свойства канала. Сетка, стыки и заставки сюда не входят —
/// они общие и запрашиваются отдельно.
/// </summary>
public class ChannelDetailsTests
{
private static readonly DateTimeOffset T0 = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
[Fact]
public async Task GetChannel_MapsSettingsAndBumperTemplates()
public async Task GetChannel_MapsOwnSettings()
{
var fixture = new TestDb();
var channel = Channel.Create("Первый", "one", T0);
var fillerId = Guid.NewGuid();
var logoId = Guid.NewGuid();
channel.UpdateSettings("Первый", isEnabled: true, bumpersEnabled: true, fillerId);
channel.UpdateSettings("Первый", isEnabled: true, fillerId);
channel.UpdateTimeSettings(3, 120, new TimeOnly(5, 0));
channel.UpdateBumperSettings(BumperFont.Serif, BumperSelection.Random);
channel.UpdateViewerSettings(logoId, LogoCorner.BottomLeft, 0.4, showClock: true, 0.25);
// Второй блок должен приехать после дефолтного — порядок задаёт позиция.
var extra = channel.AddBumperTemplate("Ночной");
await using (var seed = fixture.New())
{
@@ -48,22 +45,11 @@ public class ChannelDetailsTests
Assert.Equal(120, dto.UtcOffsetMinutes);
Assert.Equal(new TimeOnly(5, 0), dto.DayStartTime);
Assert.Equal(fillerId, dto.FillerAssetId);
Assert.True(dto.BumpersEnabled);
Assert.Equal(BumperFont.Serif, dto.Bumper.Font);
Assert.Equal(BumperSelection.Random, dto.Bumper.Selection);
Assert.Equal(logoId, dto.Viewer.LogoImageId);
Assert.Equal(LogoCorner.BottomLeft, dto.Viewer.LogoCorner);
Assert.Equal(0.4, dto.Viewer.LogoOpacity);
Assert.True(dto.Viewer.ShowClock);
Assert.Equal(0.25, dto.Viewer.AnalogFilterStrength);
Assert.Equal(2, dto.BumperTemplates.Count);
Assert.True(dto.BumperTemplates[0].IsDefault);
Assert.Equal(extra.Id, dto.BumperTemplates[1].Id);
Assert.Equal("Ночной", dto.BumperTemplates[1].Name);
// Своего звука у свежего блока нет — джингл синтезируется.
Assert.False(dto.BumperTemplates[1].HasAudio);
Assert.NotEmpty(dto.BumperTemplates[0].Variants);
}
[Fact]
@@ -48,14 +48,7 @@ public class ChannelHandlersTests
await using var db = fixture.New();
var result = await new UpdateChannelSettingsCommandHandler(db).Handle(
new UpdateChannelSettingsCommand(
channel.Id,
"c",
true,
true,
new BumperSettingsInput(BumperFont.Sans, BumperSelection.WeightedRandom),
null
),
new UpdateChannelSettingsCommand(channel.Id, "c2", true, null),
CancellationToken.None
);
Assert.True(result.IsSuccess);
@@ -63,7 +56,7 @@ public class ChannelHandlersTests
await using var verify = fixture.New();
var stored = await verify.Channels.FindAsync(channel.Id);
Assert.Equal(BumperFont.Sans, stored!.BumperFont);
Assert.Equal(BumperSelection.WeightedRandom, stored.BumperSelection);
Assert.Equal("c2", stored!.Name);
Assert.True(stored.IsEnabled);
}
}
@@ -61,8 +61,19 @@ public class QueryHandlersTests
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 bumperTemplate = BumperTemplate.Create("Блок", "Текст 1");
var variant = bumperTemplate.Variants[0];
var asset = MediaAsset.Register("Show.A.S01E01.mkv", ".mkv", MediaSource.Upload);
var bumperAsset = MediaAsset.RegisterGenerated("Блок: СЕЙЧАС / Show A");
// Метка в расписании берётся из кэша заставки — там лежит ровно тот текст, что играл.
var cache = BumperAsset.Create(
bumperTemplate.Id,
variant.Id,
"sig",
"""[{"style":"Label","color":"Accent","text":"СЕЙЧАС"},{"style":"Title","color":"Text","text":"Show A"}]""",
null,
bumperAsset.Id
);
var program = ScheduleEntry.Program(
channel.Id,
asset.Id,
@@ -73,7 +84,7 @@ public class QueryHandlersTests
);
var bumper = ScheduleEntry.Bumper(
channel.Id,
Guid.NewGuid(),
bumperAsset.Id,
T0.AddMinutes(20),
T0.AddMinutes(20).AddSeconds(8),
show.Id,
@@ -83,7 +94,10 @@ public class QueryHandlersTests
{
seed.Shows.Add(show);
seed.Channels.Add(channel);
seed.BumperTemplates.Add(bumperTemplate);
seed.MediaAssets.Add(asset);
seed.MediaAssets.Add(bumperAsset);
seed.BumperAssets.Add(cache);
seed.ScheduleEntries.Add(program);
seed.ScheduleEntries.Add(bumper);
await seed.SaveChangesAsync(CancellationToken.None);
@@ -98,7 +112,8 @@ public class QueryHandlersTests
Assert.True(result.IsSuccess);
Assert.Equal(2, result.Value.Count);
var bumperDto = result.Value.Single(e => e.Kind == ScheduleEntryKind.Bumper);
Assert.NotNull(bumperDto.BumperName);
Assert.Equal("Текст 1", bumperDto.BumperName);
Assert.Equal("СЕЙЧАС · Show A", bumperDto.BumperText);
}
[Fact]
@@ -150,52 +165,4 @@ public class QueryHandlersTests
);
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);
}
}
}
@@ -125,7 +125,7 @@ public class DeleteGuardsTests
var fixture = new TestDb();
var asset = MediaAsset.Register("filler.mkv", ".mkv", MediaSource.Upload);
var channel = Channel.Create("c", "c", T0);
channel.UpdateSettings(channel.Name, isEnabled: true, bumpersEnabled: false, asset.Id);
channel.UpdateSettings(channel.Name, isEnabled: true, asset.Id);
await using (var seed = fixture.New())
{
@@ -52,7 +52,7 @@ public class EntryTraceTests
var show = Show.Create("Фильм", ShowKind.Single);
var asset = MediaAsset.Register("film.mkv", ".mkv", MediaSource.Upload);
var junction = JunctionTemplate.Create(channel.Id, "Прайм");
var junction = JunctionTemplate.Create("Прайм");
var template = ScheduleTemplate.Create(channel.Id, "Сетка");
var layer = template.AddLayer("Прайм", 10);
var slot = layer.AddSlot("Вечернее кино", new TimeOnly(20, 0), 120);
@@ -31,8 +31,8 @@ public class ListPublicChannelsTests
var disabled = Channel.Create("Выключенный", "off", T0);
foreach (var channel in new[] { numbered, first, unnumbered })
channel.UpdateSettings(channel.Name, isEnabled: true, bumpersEnabled: false, null);
disabled.UpdateSettings("Выключенный", isEnabled: false, bumpersEnabled: false, null);
channel.UpdateSettings(channel.Name, isEnabled: true, null);
disabled.UpdateSettings("Выключенный", isEnabled: false, null);
await using (var seed = fixture.New())
{
@@ -57,7 +57,7 @@ public class ListPublicChannelsTests
{
var fixture = new TestDb();
var channel = Channel.Create("Первый", "one", T0);
channel.UpdateSettings("Первый", isEnabled: true, bumpersEnabled: false, null);
channel.UpdateSettings("Первый", isEnabled: true, null);
var logoId = Guid.NewGuid();
channel.UpdateViewerSettings(logoId, LogoCorner.TopRight, 0.5, showClock: true, 0.3);
@@ -11,44 +11,57 @@ namespace TeleWave.Application.Tests.Validators;
public class ValidatorTests
{
[Fact]
public void UpdateBumperTextVariant_ChecksLengthsAndWeight()
public void UpdateBumperVariant_ChecksNameWeightAndLines()
{
var v = new UpdateBumperTextVariantCommandValidator();
var v = new UpdateBumperVariantCommandValidator();
var good = new UpdateBumperTextVariantCommand(
var good = new UpdateBumperVariantCommand(
Guid.NewGuid(),
Guid.NewGuid(),
Guid.NewGuid(),
"Name",
BumperTextKind.NowNext,
"NOW",
"NEXT",
"l1",
"l2",
BumperTrigger.Both,
3
new BumperVariantInput(
"Name",
BumperTrigger.Both,
BumperBackground.NextPoster,
3,
[new BumperLineDto(BumperLineStyle.Title, BumperLineColor.Text, "{next.title}")]
)
);
Assert.True(v.Validate(good).IsValid);
Assert.False(v.Validate(good with { Name = "" }).IsValid);
Assert.False(v.Validate(good with { Weight = -1 }).IsValid);
Assert.False(v.Validate(good with { Line1 = new string('x', 121) }).IsValid);
Assert.False(v.Validate(Patch(good, input => input with { Name = "" })).IsValid);
Assert.False(v.Validate(Patch(good, input => input with { Weight = -1 })).IsValid);
Assert.False(
v.Validate(
Patch(
good,
input =>
input with
{
Lines =
[
new BumperLineDto(
BumperLineStyle.Title,
BumperLineColor.Text,
new string('x', 121)
),
],
}
)
).IsValid
);
}
private static UpdateBumperVariantCommand Patch(
UpdateBumperVariantCommand command,
Func<BumperVariantInput, BumperVariantInput> change
) => command with { Input = change(command.Input) };
[Fact]
public void UpdateChannelSettings_ChecksRanges()
{
var v = new UpdateChannelSettingsCommandValidator();
var bumper = new BumperSettingsInput(BumperFont.Sans, BumperSelection.WeightedRandom);
var good = new UpdateChannelSettingsCommand(
Guid.NewGuid(),
"Name",
true,
true,
bumper,
null
);
var good = new UpdateChannelSettingsCommand(Guid.NewGuid(), "Name", true, null);
Assert.True(v.Validate(good).IsValid);
Assert.False(v.Validate(good with { Name = "" }).IsValid);