Files
TeleWave/backend/tests/TeleWave.Application.Tests/Programming/PlanningRulesTests.cs
T

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));
}
}