Files
TeleWave/backend/tests/TeleWave.Application.Tests/Programming/LayerApplicabilityTests.cs
T
Leonid Pershin 7bee84c548
ci / build-backend (push) Successful in 2m37s
ci / build-frontend (push) Successful in 1m11s
ci / tests (push) Successful in 2m31s
ci / sonar (push) Successful in 6m52s
Enhance EffectiveGridBuilder to support background slots and improve scheduling logic
Updated the EffectiveGridBuilder to return an EffectiveGrid containing both scheduled and background slots, allowing for better management of overlapping content. Refactored the ResolveDay method to handle background slots separately, ensuring they fill gaps in the schedule appropriately. Adjusted related components, including GridScheduleGenerator and SchedulePlanner, to accommodate the new structure and improve overall scheduling accuracy. Enhanced documentation and comments for clarity.
2026-07-28 01:06:29 +03:00

147 lines
5.9 KiB
C#

using TeleWave.Application.Programming.Planning;
using TeleWave.Application.Programming.Templates;
using TeleWave.Domain.Programming;
using Xunit;
namespace TeleWave.Application.Tests.Programming;
/// <summary>
/// Применимость слоёв (см. 3.4) и разрешение перекрытий: какие слоты реально действуют в сутки.
/// </summary>
public class LayerApplicabilityTests
{
private static readonly TimeOnly DayStart = new(6, 0);
[Fact]
public void Empty_CoversEveryDate()
{
var applicability = new LayerApplicability();
Assert.True(applicability.IsEmpty);
Assert.True(applicability.Covers(new DateOnly(2026, 3, 17)));
}
[Theory]
[InlineData(2026, 12, 25, true)] // внутри, до Нового года
[InlineData(2026, 1, 3, true)] // внутри, после Нового года
[InlineData(2026, 12, 20, true)] // ровно начало
[InlineData(2026, 1, 8, true)] // ровно конец
[InlineData(2026, 12, 19, false)] // за день до начала
[InlineData(2026, 1, 9, false)] // на следующий день после конца
[InlineData(2026, 6, 15, false)] // середина года
public void AnnualRange_CrossingNewYear_IsInclusiveOnBothEnds(
int year,
int month,
int day,
bool expected
)
{
// «20 декабря — 8 января» задаётся один раз и работает в любом году, поэтому сравнение идёт
// по паре (месяц, день), а не по датам.
var applicability = new LayerApplicability(AnnualRanges: [new AnnualRange(12, 20, 1, 8)]);
Assert.Equal(expected, applicability.Covers(new DateOnly(year, month, day)));
}
[Fact]
public void AnnualRange_WithinOneYear_DoesNotWrap()
{
var applicability = new LayerApplicability(AnnualRanges: [new AnnualRange(6, 1, 8, 31)]);
Assert.True(applicability.Covers(new DateOnly(2026, 7, 4)));
Assert.False(applicability.Covers(new DateOnly(2026, 1, 4)));
}
[Fact]
public void Sections_AreCombinedWithOr()
{
// Понедельник ИЛИ конкретная дата: суббота из списка дат проходит, обычная суббота — нет.
var applicability = new LayerApplicability(
Weekdays: [1],
SpecificDates: [new DateOnly(2026, 3, 21)]
);
Assert.True(applicability.Covers(new DateOnly(2026, 3, 16))); // понедельник
Assert.True(applicability.Covers(new DateOnly(2026, 3, 21))); // суббота из списка
Assert.False(applicability.Covers(new DateOnly(2026, 3, 14))); // другая суббота
}
[Fact]
public void Build_SkipsSlotsCoveredByHigherPriorityLayer()
{
// Слот младшего слоя пропускается целиком, а не обрезается: половина слота означала бы
// половину настройки — своей группы и стратегии у половинки нет.
var template = ScheduleTemplate.Create(Guid.NewGuid(), "Сетка");
var top = template.AddLayer("Прайм", 100);
var bottom = template.AddLayer("Обычный", 50);
AddSlot(top, "Кино", new TimeOnly(20, 0), 120);
AddSlot(bottom, "Сериал", new TimeOnly(20, 30), 60);
AddSlot(bottom, "Ночь", new TimeOnly(23, 0), 60);
var titles = Build(template).Select(s => s.Slot.Title).Distinct().ToList();
Assert.Contains("Кино", titles);
Assert.Contains("Ночь", titles);
Assert.DoesNotContain("Сериал", titles);
}
[Fact]
public void Build_DropsLayersThatDoNotApplyOnTheDate()
{
var template = ScheduleTemplate.Create(Guid.NewGuid(), "Сетка");
var newYear = template.AddLayer("Новогодний", 100);
newYear.Update(
newYear.Name,
newYear.Priority,
new LayerApplicability(AnnualRanges: [new AnnualRange(12, 20, 1, 8)]).ToJson(),
isEnabled: true
);
AddSlot(newYear, "Ирония судьбы", new TimeOnly(20, 0), 180);
var usual = template.AddLayer("Обычный", 50);
AddSlot(usual, "Вечерний сериал", new TimeOnly(20, 0), 60);
var march = Build(template, new DateTimeOffset(2026, 3, 17, 12, 0, 0, TimeSpan.Zero))
.Select(s => s.Slot.Title)
.ToList();
var december = Build(template, new DateTimeOffset(2026, 12, 25, 12, 0, 0, TimeSpan.Zero))
.Select(s => s.Slot.Title)
.ToList();
Assert.Contains("Вечерний сериал", march);
Assert.DoesNotContain("Ирония судьбы", march);
Assert.Contains("Ирония судьбы", december);
Assert.DoesNotContain("Вечерний сериал", december);
}
[Fact]
public void Build_DisabledLayerIsIgnored()
{
var template = ScheduleTemplate.Create(Guid.NewGuid(), "Сетка");
var layer = template.AddLayer("Выключённый", 100);
AddSlot(layer, "Ничего", new TimeOnly(20, 0), 60);
layer.Update(layer.Name, layer.Priority, null, isEnabled: false);
Assert.Empty(Build(template));
}
private static void AddSlot(
GridLayer layer,
string title,
TimeOnly start,
int durationMinutes
) => layer.AddSlot(title, start, durationMinutes);
private static IReadOnlyList<ScheduledSlot> Build(
ScheduleTemplate template,
DateTimeOffset? from = null
)
{
var start = from ?? new DateTimeOffset(2026, 3, 17, 12, 0, 0, TimeSpan.Zero);
return EffectiveGridBuilder
.Build(template, utcOffsetMinutes: 180, DayStart, start, start.AddHours(18))
.Slots;
}
}