108 lines
4.4 KiB
C#
108 lines
4.4 KiB
C#
using TeleWave.Application.Programming.Templates;
|
|
using TeleWave.Domain.Library;
|
|
using Xunit;
|
|
|
|
namespace TeleWave.Application.Tests.Programming;
|
|
|
|
/// <summary>Окна детского времени: разрешение по времени суток, включая переход через полночь.</summary>
|
|
public class PlanningRulesTests
|
|
{
|
|
[Theory]
|
|
[InlineData(7, 0, true)]
|
|
[InlineData(22, 59, true)]
|
|
[InlineData(6, 0, true)] // ровно начало
|
|
[InlineData(23, 0, false)] // ровно конец — уже вне окна
|
|
[InlineData(2, 0, false)]
|
|
public void AudienceAt_DayWindow(int hour, int minute, bool inside)
|
|
{
|
|
var rules = new PlanningRules(
|
|
[new AudienceWindow(new TimeOnly(6, 0), new TimeOnly(23, 0), ShowAudience.Pg13)]
|
|
);
|
|
|
|
var result = rules.AudienceAt(new TimeOnly(hour, minute));
|
|
|
|
Assert.Equal(inside ? ShowAudience.Pg13 : null, result);
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(23, 30, true)]
|
|
[InlineData(3, 0, true)]
|
|
[InlineData(12, 0, false)]
|
|
public void AudienceAt_WindowCrossingMidnight(int hour, int minute, bool inside)
|
|
{
|
|
// «С 23:00 до 06:00» — ночное окно, границы сравниваются в обратную сторону.
|
|
var rules = new PlanningRules(
|
|
[new AudienceWindow(new TimeOnly(23, 0), new TimeOnly(6, 0), ShowAudience.Nc17)]
|
|
);
|
|
|
|
var result = rules.AudienceAt(new TimeOnly(hour, minute));
|
|
|
|
Assert.Equal(inside ? ShowAudience.Nc17 : null, result);
|
|
}
|
|
|
|
[Fact]
|
|
public void AudienceAt_OverlappingWindows_TakesTheStrictest()
|
|
{
|
|
// Широкое окно, случайно наложенное поверх детского, не должно его отменять.
|
|
var rules = new PlanningRules(
|
|
[
|
|
new AudienceWindow(new TimeOnly(6, 0), new TimeOnly(23, 0), ShowAudience.R),
|
|
new AudienceWindow(new TimeOnly(7, 0), new TimeOnly(10, 0), ShowAudience.G),
|
|
]
|
|
);
|
|
|
|
Assert.Equal(ShowAudience.G, rules.AudienceAt(new TimeOnly(8, 0)));
|
|
Assert.Equal(ShowAudience.R, rules.AudienceAt(new TimeOnly(12, 0)));
|
|
}
|
|
|
|
[Fact]
|
|
public void AudienceAt_WithoutWindows_IsUnlimited()
|
|
{
|
|
Assert.Null(new PlanningRules().AudienceAt(new TimeOnly(3, 0)));
|
|
}
|
|
|
|
[Fact]
|
|
public void Json_RoundTrips()
|
|
{
|
|
var rules = new PlanningRules(
|
|
[new AudienceWindow(new TimeOnly(6, 0), new TimeOnly(23, 0), ShowAudience.Pg13)],
|
|
new RepeatLimitRule(7, 2),
|
|
MaxBreakMinutesPerHour: 12,
|
|
MaxGenreSharePercent: 40,
|
|
MaxFallbackSharePercent: 15
|
|
);
|
|
|
|
var restored = PlanningRules.FromJson(rules.ToJson());
|
|
|
|
// Сравниваем по полям: у record со списком внутри равенство ссылочное, и `Assert.Equal`
|
|
// на самих правилах проверял бы не то.
|
|
Assert.NotNull(restored);
|
|
Assert.Equal(rules.MaxAudienceByTime!, restored.MaxAudienceByTime!);
|
|
Assert.Equal(rules.MaxRepeatsInWindow, restored.MaxRepeatsInWindow);
|
|
Assert.Equal(rules.MaxBreakMinutesPerHour, restored.MaxBreakMinutesPerHour);
|
|
Assert.Equal(rules.MaxGenreSharePercent, restored.MaxGenreSharePercent);
|
|
Assert.Equal(rules.MaxFallbackSharePercent, restored.MaxFallbackSharePercent);
|
|
}
|
|
|
|
[Fact]
|
|
public void Json_WritesMpaaSpelling()
|
|
{
|
|
// Рейтинг уезжает в jsonb шаблона и в API ровно тем написанием, каким приходит от источников.
|
|
// Round-trip этого не поймает: он одинаково зелёный и на «Pg13», а такое значение потом
|
|
// придётся переводить на каждой границе.
|
|
var json = new PlanningRules(
|
|
[new AudienceWindow(new TimeOnly(6, 0), new TimeOnly(23, 0), ShowAudience.Pg13)]
|
|
).ToJson();
|
|
|
|
Assert.Contains("\"PG-13\"", json, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void FromJson_Garbage_IsNull()
|
|
{
|
|
// Битый JSON не должен ронять генерацию: правило просто считается незаданным.
|
|
Assert.Null(PlanningRules.FromJson("{"));
|
|
Assert.Null(PlanningRules.FromJson(null));
|
|
}
|
|
}
|