Files
TeleWave/backend/tests/TeleWave.Application.Tests/Programming/EntryTraceTests.cs
T
Leonid Pershin ba3721eb92
ci / build-backend (push) Successful in 1m39s
ci / build-frontend (push) Failing after 26s
ci / tests (push) Skipped
ci / sonar (push) Skipped
Implement BumperEndpoints and remove deprecated bumper-related functionality
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.
2026-07-27 22:01:56 +03:00

230 lines
8.5 KiB
C#

using System.Text.Json;
using System.Text.Json.Serialization;
using TeleWave.Application.Broadcast;
using TeleWave.Application.Programming.Planning.Trace;
using TeleWave.Application.Programming.Templates;
using TeleWave.Application.Tests.Support;
using TeleWave.Domain.Broadcast;
using TeleWave.Domain.Library;
using TeleWave.Domain.Media;
using TeleWave.Domain.Programming;
using TeleWave.Domain.Programming.Planning;
using Xunit;
namespace TeleWave.Application.Tests.Programming;
/// <summary>
/// «Почему это здесь»: трейс пишется в момент генерации и разбирается вместе с текущим состоянием
/// сетки. Слот мог быть с тех пор изменён или удалён — подписи тогда пустеют, но ответ остаётся.
/// </summary>
public class EntryTraceTests
{
private static readonly DateTimeOffset T0 = new(2026, 1, 1, 18, 0, 0, TimeSpan.Zero);
/// <summary>Те же настройки, что при записи трейса генератором.</summary>
private static readonly JsonSerializerOptions TraceJson = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Converters = { new JsonStringEnumConverter() },
};
[Fact]
public async Task Trace_UnknownEntry_ReturnsNotFound()
{
var fixture = new TestDb();
await using var db = fixture.New();
var result = await new GetEntryTraceQueryHandler(db).Handle(
new GetEntryTraceQuery(Guid.NewGuid()),
CancellationToken.None
);
Assert.Equal(ChannelErrors.NotFound, result.Error);
}
[Fact]
public async Task Trace_ResolvesSlotLayerGroupAndJunction()
{
var fixture = new TestDb();
var channel = Channel.Create("Первый", "one", T0);
var group = Group.Create("Кино");
var collection = Collection.Create("Франшиза");
var show = Show.Create("Фильм", ShowKind.Single);
var asset = MediaAsset.Register("film.mkv", ".mkv", MediaSource.Upload);
var junction = JunctionTemplate.Create("Прайм");
var template = ScheduleTemplate.Create(channel.Id, "Сетка");
var layer = template.AddLayer("Прайм", 10);
var slot = layer.AddSlot("Вечернее кино", new TimeOnly(20, 0), 120);
slot.UpdateContent(
new SlotContent(
slot.Title,
SlotKind.Content,
group.Id,
new SlotStrategy(SlotStrategyType.RandomWithCooldown, CooldownDays: 14).ToJson(),
null,
SlotBlockMode.FillSlot,
1,
OverflowPolicy.ContinueNext,
JunctionAfterId: junction.Id
)
);
var trace = new PlanTrace(
slot.Id,
SlotKind.Content,
GroupElementKind.Show,
show.Id,
SlotStrategyKind.RandomWithCooldown,
CandidatesAfterCooldown: 4,
DriftMinutes: 3,
Snapped: true
);
var entry = ScheduleEntry.FromSlot(
channel.Id,
asset.Id,
ScheduleEntryKind.Program,
T0,
T0.AddMinutes(90),
new ScheduleEntryOrigin(
show.Id,
2,
slot.Id,
JsonSerializer.Serialize(trace, TraceJson),
collection.Id
)
);
await using (var seed = fixture.New())
{
seed.Channels.Add(channel);
seed.Groups.Add(group);
seed.Collections.Add(collection);
seed.Shows.Add(show);
seed.MediaAssets.Add(asset);
seed.JunctionTemplates.Add(junction);
seed.ScheduleTemplates.Add(template);
seed.ScheduleEntries.Add(entry);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var result = await new GetEntryTraceQueryHandler(db).Handle(
new GetEntryTraceQuery(entry.Id),
CancellationToken.None
);
Assert.True(result.IsSuccess);
var dto = result.Value;
Assert.Equal("Фильм", dto.ShowName);
Assert.Equal(2, dto.EpisodeIndex);
Assert.Equal("Франшиза", dto.CollectionName);
Assert.Equal("Прайм", dto.LayerName);
Assert.Equal(10, dto.LayerPriority);
Assert.Equal("Вечернее кино", dto.SlotTitle);
Assert.Equal(new TimeOnly(20, 0), dto.SlotTargetStart);
Assert.Equal(120, dto.SlotDurationMinutes);
Assert.Equal("Кино", dto.GroupName);
Assert.Equal(SlotStrategyKind.RandomWithCooldown, dto.Strategy);
// Остывание берётся из текущей стратегии слота, а число кандидатов — из трейса.
Assert.Equal(14, dto.CooldownDays);
Assert.Equal(4, dto.CandidatesAfterCooldown);
Assert.Equal(3, dto.DriftMinutes);
Assert.True(dto.Snapped);
Assert.Equal("Прайм", dto.JunctionName);
}
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("{не json}")]
public async Task Trace_WithoutUsableTrace_ReturnsBareEntry(string? traceJson)
{
var fixture = new TestDb();
var channel = Channel.Create("Первый", "one", T0);
var show = Show.Create("Фильм", ShowKind.Single);
var asset = MediaAsset.Register("film.mkv", ".mkv", MediaSource.Upload);
var entry = ScheduleEntry.FromSlot(
channel.Id,
asset.Id,
ScheduleEntryKind.Program,
T0,
T0.AddMinutes(90),
new ScheduleEntryOrigin(show.Id, 1, null, traceJson)
);
await using (var seed = fixture.New())
{
seed.Channels.Add(channel);
seed.Shows.Add(show);
seed.MediaAssets.Add(asset);
seed.ScheduleEntries.Add(entry);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var result = await new GetEntryTraceQueryHandler(db).Handle(
new GetEntryTraceQuery(entry.Id),
CancellationToken.None
);
// Старая запись (или битый трейс) — ответ есть, подписи пустые.
Assert.True(result.IsSuccess);
Assert.Equal("Фильм", result.Value.ShowName);
Assert.Null(result.Value.LayerName);
Assert.Null(result.Value.SlotTitle);
Assert.Null(result.Value.Strategy);
Assert.Equal(0, result.Value.DriftMinutes);
Assert.False(result.Value.Snapped);
}
[Fact]
public async Task Trace_WithDeletedSlot_KeepsWhatTraceRemembers()
{
var fixture = new TestDb();
var channel = Channel.Create("Первый", "one", T0);
var asset = MediaAsset.Register("film.mkv", ".mkv", MediaSource.Upload);
var trace = new PlanTrace(
Guid.NewGuid(),
SlotKind.Repeat,
null,
null,
SlotStrategyKind.Sequential,
null,
DriftMinutes: 0,
Snapped: false
);
var entry = ScheduleEntry.FromSlot(
channel.Id,
asset.Id,
ScheduleEntryKind.Program,
T0,
T0.AddMinutes(30),
new ScheduleEntryOrigin(null, null, null, JsonSerializer.Serialize(trace, TraceJson))
);
await using (var seed = fixture.New())
{
seed.Channels.Add(channel);
seed.MediaAssets.Add(asset);
seed.ScheduleEntries.Add(entry);
await seed.SaveChangesAsync(CancellationToken.None);
}
await using var db = fixture.New();
var result = await new GetEntryTraceQueryHandler(db).Handle(
new GetEntryTraceQuery(entry.Id),
CancellationToken.None
);
Assert.True(result.IsSuccess);
// Слота уже нет — из трейса остаётся его тип и стратегия, остальное пустеет.
Assert.Equal(SlotKind.Repeat, result.Value.SlotKind);
Assert.Equal(SlotStrategyKind.Sequential, result.Value.Strategy);
Assert.Null(result.Value.SlotTitle);
Assert.Null(result.Value.GroupName);
Assert.Null(result.Value.JunctionName);
Assert.Null(result.Value.ShowName);
}
}