Add project reference for TeleWave.Infrastructure: include TeleWave.Infrastructure.csproj in TeleWave.Application.Tests project to support additional testing capabilities.

This commit is contained in:
Leonid Pershin
2026-07-25 20:12:04 +03:00
parent ed240ef73c
commit 4611f90d11
18 changed files with 2027 additions and 0 deletions
@@ -0,0 +1,115 @@
using TeleWave.Domain.Broadcast;
using Xunit;
namespace TeleWave.Domain.Tests.Broadcast;
public class BumperTemplateTests
{
private static BumperTemplate NewTemplate() =>
Channel.Create("c", "c", DateTimeOffset.UnixEpoch).AddBumperTemplate("Block");
[Fact]
public void Create_HasDefaultsAndOneVariant()
{
var t = NewTemplate();
Assert.Equal(BumperTemplate.DefaultBackgroundColor, t.BackgroundColor);
Assert.Null(t.AudioExtension);
Assert.Null(t.BackgroundImageId);
Assert.Equal(0, t.Revision);
var v = Assert.Single(t.Variants);
Assert.Equal(BumperTrigger.OnShowChange, v.Trigger);
}
[Fact]
public void AddVariant_AppendsWithNextPosition()
{
var t = NewTemplate();
var v1 = t.AddVariant("Text 2");
Assert.Equal(1, v1.Position);
Assert.Equal(2, t.Variants.Count);
Assert.Equal(v1, t.FindVariant(v1.Id));
Assert.Null(t.FindVariant(Guid.NewGuid()));
}
[Fact]
public void RemoveVariant_CannotRemoveLast()
{
var t = NewTemplate();
var first = t.Variants[0];
Assert.False(t.RemoveVariant(first.Id)); // единственный — нельзя
var second = t.AddVariant("Text 2");
Assert.True(t.RemoveVariant(second.Id));
Assert.False(t.RemoveVariant(Guid.NewGuid()));
Assert.Single(t.Variants);
}
[Fact]
public void UpdateStyle_ChangesColorsAndName()
{
var t = NewTemplate();
t.UpdateStyle("New", "0x111111", "0x222222", "0x333333", "black");
Assert.Equal("New", t.Name);
Assert.Equal("0x111111", t.BackgroundColor);
Assert.Equal("0x333333", t.AccentColor);
Assert.Equal("black", t.TextColor);
}
[Fact]
public void SetAudio_BumpsRevision_AndStoresDuration()
{
var t = NewTemplate();
t.SetAudio(".mp3", 12.5);
Assert.Equal(".mp3", t.AudioExtension);
Assert.Equal(12.5, t.AudioDurationSeconds);
Assert.Equal(1, t.Revision);
}
[Fact]
public void SetAudio_NonPositiveDuration_StoresNull()
{
var t = NewTemplate();
t.SetAudio(".wav", 0);
Assert.Null(t.AudioDurationSeconds);
}
[Fact]
public void ClearAudio_ResetsAndBumpsRevisionOnceWhenPresent()
{
var t = NewTemplate();
t.SetAudio(".mp3", 5); // rev 1
t.ClearAudio(); // rev 2
Assert.Null(t.AudioExtension);
Assert.Equal(2, t.Revision);
t.ClearAudio(); // уже пусто — ревизия не двигается
Assert.Equal(2, t.Revision);
}
[Fact]
public void BackgroundImage_SetAndClear_BumpRevision()
{
var t = NewTemplate();
var img = Guid.NewGuid();
t.SetBackgroundImage(img); // rev 1
Assert.Equal(img, t.BackgroundImageId);
Assert.Equal(1, t.Revision);
t.ClearBackgroundImage(); // rev 2
Assert.Null(t.BackgroundImageId);
Assert.Equal(2, t.Revision);
t.ClearBackgroundImage(); // уже пусто
Assert.Equal(2, t.Revision);
}
}
@@ -0,0 +1,55 @@
using TeleWave.Domain.Broadcast;
using Xunit;
namespace TeleWave.Domain.Tests.Broadcast;
public class BumperTextVariantTests
{
private static BumperTextVariant NewVariant() =>
Channel
.Create("c", "c", DateTimeOffset.UnixEpoch)
.AddBumperTemplate("b")
.AddVariant("Text");
[Fact]
public void Create_HasDefaultLabelsAndWeight()
{
var v = NewVariant();
Assert.Equal(BumperTextKind.NowNext, v.Kind);
Assert.Equal(BumperTextVariant.DefaultNowLabel, v.NowLabel);
Assert.Equal(BumperTextVariant.DefaultNextLabel, v.NextLabel);
Assert.Equal(BumperTextVariant.DefaultWeight, v.Weight);
Assert.Equal(string.Empty, v.Line1);
}
[Fact]
public void Update_SetsFields_AndClampsNegativeWeightToZero()
{
var v = NewVariant();
v.Update("Name", BumperTextKind.Free, "NOW", "NEXT", "l1", "l2", BumperTrigger.Both, -5);
Assert.Equal("Name", v.Name);
Assert.Equal(BumperTextKind.Free, v.Kind);
Assert.Equal("NOW", v.NowLabel);
Assert.Equal("l1", v.Line1);
Assert.Equal(BumperTrigger.Both, v.Trigger);
Assert.Equal(0, v.Weight);
}
[Theory]
[InlineData(BumperTrigger.OnShowChange, true, true)]
[InlineData(BumperTrigger.OnShowChange, false, false)]
[InlineData(BumperTrigger.BetweenEpisodes, true, false)]
[InlineData(BumperTrigger.BetweenEpisodes, false, true)]
[InlineData(BumperTrigger.Both, true, true)]
[InlineData(BumperTrigger.Both, false, true)]
public void Matches_FollowsTrigger(BumperTrigger trigger, bool isShowChange, bool expected)
{
var v = NewVariant();
v.Update("n", BumperTextKind.NowNext, "a", "b", "", "", trigger, 1);
Assert.Equal(expected, v.Matches(isShowChange));
}
}
@@ -0,0 +1,89 @@
using TeleWave.Domain.Broadcast;
using Xunit;
namespace TeleWave.Domain.Tests.Broadcast;
public class ChannelShowTests
{
private static ChannelShow NewShow() =>
Channel
.Create("c", "c", DateTimeOffset.UnixEpoch)
.AddShow(Guid.NewGuid(), 1, BlockMode.Count, 1);
[Fact]
public void Create_HasDefaultPreferredMultiplier_AndNoWindows()
{
var cs = NewShow();
Assert.Equal(ChannelShow.DefaultPreferredWeightMultiplier, cs.PreferredWeightMultiplier);
Assert.Empty(cs.PreferredHours);
Assert.True(cs.IsEnabled);
Assert.Equal(0, cs.NextEpisodeIndex);
}
[Fact]
public void Update_ChangesCoreFields()
{
var cs = NewShow();
cs.Update(9, BlockMode.Duration, 45, false);
Assert.Equal(9, cs.Weight);
Assert.Equal(BlockMode.Duration, cs.BlockMode);
Assert.Equal(45, cs.BlockValue);
Assert.False(cs.IsEnabled);
}
[Fact]
public void SetPreferredHours_ClampsMultiplier_AndNormalizesWindows()
{
var cs = NewShow();
cs.SetPreferredHours(
0, // < 1 → должно стать 1
[
(18, 23), // валидное
(10, 10), // start == end → отбрасывается
(5, 25), // end > 24 → отбрасывается
(-1, 3), // start < 0 → отбрасывается
(18, 23), // дубликат → схлопывается
]
);
Assert.Equal(1, cs.PreferredWeightMultiplier);
var window = Assert.Single(cs.PreferredHours);
Assert.Equal(18, window.StartHour);
Assert.Equal(23, window.EndHour);
}
[Fact]
public void SetPreferredHours_ReplacesPreviousSet()
{
var cs = NewShow();
cs.SetPreferredHours(3, [(1, 2)]);
cs.SetPreferredHours(2, [(8, 9), (20, 22)]);
Assert.Equal(2, cs.PreferredWeightMultiplier);
Assert.Equal(2, cs.PreferredHours.Count);
}
[Fact]
public void IsPreferredAt_ChecksWindows()
{
var cs = NewShow();
cs.SetPreferredHours(3, [(18, 23)]);
Assert.True(cs.IsPreferredAt(18));
Assert.True(cs.IsPreferredAt(22));
Assert.False(cs.IsPreferredAt(23)); // конец исключается
Assert.False(cs.IsPreferredAt(9));
}
[Fact]
public void SetNextEpisodeIndex_MovesCursor()
{
var cs = NewShow();
cs.SetNextEpisodeIndex(5);
Assert.Equal(5, cs.NextEpisodeIndex);
}
}
@@ -0,0 +1,167 @@
using TeleWave.Domain.Broadcast;
using Xunit;
namespace TeleWave.Domain.Tests.Broadcast;
public class ChannelTests
{
private static readonly DateTimeOffset Epoch = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
private static Channel NewChannel() => Channel.Create("News", "news", Epoch);
[Fact]
public void Create_SetsDefaults_AndSeedsDefaultBumperTemplate()
{
var channel = NewChannel();
Assert.NotEqual(Guid.Empty, channel.Id);
Assert.Equal("News", channel.Name);
Assert.Equal("news", channel.Slug);
Assert.True(channel.IsEnabled);
Assert.Equal(Epoch, channel.EpochUtc);
Assert.Equal(AdInsertion.BetweenBlocks, channel.AdInsertion);
Assert.False(channel.BumpersEnabled);
Assert.Equal(BumperSelection.Rotation, channel.BumperSelection);
Assert.Equal(1.0, channel.BumperShowChangeChance);
Assert.Equal(1.0, channel.BumperEpisodeChangeChance);
var template = Assert.Single(channel.BumperTemplates);
Assert.True(template.IsDefault);
Assert.Equal(0, template.Position);
Assert.Single(template.Variants); // дефолтный подблок
}
[Fact]
public void UpdateSettings_ChangesFields()
{
var channel = NewChannel();
var filler = Guid.NewGuid();
channel.UpdateSettings("N2", false, AdInsertion.BetweenEpisodes, 4, true, filler);
Assert.Equal("N2", channel.Name);
Assert.False(channel.IsEnabled);
Assert.Equal(AdInsertion.BetweenEpisodes, channel.AdInsertion);
Assert.Equal(4, channel.AdsPerBreak);
Assert.True(channel.BumpersEnabled);
Assert.Equal(filler, channel.FillerAssetId);
}
[Theory]
[InlineData(-1, 2.0, -5.0, 1.0, 0.0)]
[InlineData(30, 0.3, 0.9, 0.3, 0.9)]
public void UpdateBumperSettings_ClampsChancesAndInterval(
int interval,
double show,
double episode,
double expectedShow,
double expectedEpisode
)
{
var channel = NewChannel();
channel.UpdateBumperSettings(
BumperFont.Serif,
interval,
BumperSelection.WeightedRandom,
show,
episode
);
Assert.Equal(BumperFont.Serif, channel.BumperFont);
Assert.Equal(Math.Max(0, interval), channel.BumperMinIntervalMinutes);
Assert.Equal(BumperSelection.WeightedRandom, channel.BumperSelection);
Assert.Equal(expectedShow, channel.BumperShowChangeChance);
Assert.Equal(expectedEpisode, channel.BumperEpisodeChangeChance);
}
[Fact]
public void AddBumperTemplate_AppendsWithNextPosition()
{
var channel = NewChannel();
var t1 = channel.AddBumperTemplate("Block 2");
var t2 = channel.AddBumperTemplate("Block 3");
Assert.Equal(1, t1.Position);
Assert.Equal(2, t2.Position);
Assert.False(t1.IsDefault);
Assert.Equal(3, channel.BumperTemplates.Count);
Assert.Equal(t1, channel.FindBumperTemplate(t1.Id));
Assert.Null(channel.FindBumperTemplate(Guid.NewGuid()));
}
[Fact]
public void RemoveBumperTemplate_CannotRemoveDefault_CanRemoveOthers()
{
var channel = NewChannel();
var def = channel.BumperTemplates[0];
var extra = channel.AddBumperTemplate("Block 2");
Assert.False(channel.RemoveBumperTemplate(def.Id));
Assert.True(channel.RemoveBumperTemplate(extra.Id));
Assert.False(channel.RemoveBumperTemplate(Guid.NewGuid()));
Assert.Single(channel.BumperTemplates);
}
[Fact]
public void Shows_AddFindRemoveHas()
{
var channel = NewChannel();
var showId = Guid.NewGuid();
var cs = channel.AddShow(showId, 5, BlockMode.Count, 3);
Assert.Equal(showId, cs.ShowId);
Assert.Equal(5, cs.Weight);
Assert.True(channel.HasShow(showId));
Assert.Equal(cs, channel.FindShow(cs.Id));
Assert.True(channel.RemoveShow(cs.Id));
Assert.False(channel.RemoveShow(cs.Id));
Assert.False(channel.HasShow(showId));
}
[Fact]
public void Ads_AppendWithPosition_AndRemove()
{
var channel = NewChannel();
var a1 = channel.AddAd(Guid.NewGuid());
var a2 = channel.AddAd(Guid.NewGuid());
Assert.Equal(0, a1.Position);
Assert.Equal(1, a2.Position);
Assert.True(channel.HasAd(a1.MediaAssetId));
Assert.True(channel.RemoveAd(a1.Id));
Assert.False(channel.RemoveAd(a1.Id));
}
[Fact]
public void AddOverride_OneTime_AndWeekly()
{
var channel = NewChannel();
var one = channel.AddOverride(OverrideMode.Exclusive, Epoch, Epoch.AddHours(2));
Assert.Equal(OverrideRecurrence.OneTime, one.Recurrence);
Assert.Equal(Epoch, one.StartsAtUtc);
var weekly = channel.AddWeeklyOverride(OverrideMode.Boost, 6, 600, 720);
Assert.Equal(OverrideRecurrence.Weekly, weekly.Recurrence);
Assert.Equal(6, weekly.DayOfWeek);
Assert.Equal(600, weekly.StartMinute);
Assert.Equal(2, channel.Overrides.Count);
Assert.True(channel.RemoveOverride(one.Id));
Assert.False(channel.RemoveOverride(Guid.NewGuid()));
}
[Fact]
public void Cursors_AreMovedBySetters()
{
var channel = NewChannel();
channel.SetNextAdIndex(7);
channel.SetNextBumperIndex(3);
Assert.Equal(7, channel.NextAdIndex);
Assert.Equal(3, channel.NextBumperIndex);
}
}
@@ -0,0 +1,50 @@
using TeleWave.Domain.Broadcast;
using TeleWave.Domain.Settings;
using Xunit;
namespace TeleWave.Domain.Tests.Broadcast;
/// <summary>Мелкие фабрики/сеттеры простых сущностей, которые нигде не покрыты напрямую.</summary>
public class DomainRecordFactoryTests
{
[Fact]
public void ChannelShowHour_Contains_HalfOpenWindow()
{
var cs = Channel
.Create("c", "c", DateTimeOffset.UnixEpoch)
.AddShow(Guid.NewGuid(), 1, BlockMode.Count, 1);
cs.SetPreferredHours(2, [(9, 12)]);
var hour = Assert.Single(cs.PreferredHours);
Assert.False(hour.Contains(8));
Assert.True(hour.Contains(9));
Assert.True(hour.Contains(11));
Assert.False(hour.Contains(12));
}
[Fact]
public void BumperAsset_Create_StoresPair()
{
var from = Guid.NewGuid();
var to = Guid.NewGuid();
var asset = Guid.NewGuid();
var b = BumperAsset.Create(from, to, "sig", asset);
Assert.Equal(from, b.FromShowId);
Assert.Equal(to, b.ToShowId);
Assert.Equal("sig", b.Signature);
Assert.Equal(asset, b.MediaAssetId);
}
[Fact]
public void AppSetting_CreateAndSetValue()
{
var s = AppSetting.Create("site.title", "TeleWave");
Assert.Equal("site.title", s.Key);
Assert.Equal("TeleWave", s.Value);
s.SetValue("New");
Assert.Equal("New", s.Value);
}
}
@@ -0,0 +1,59 @@
using TeleWave.Domain.Broadcast;
using Xunit;
namespace TeleWave.Domain.Tests.Broadcast;
public class ProgrammingOverrideTests
{
private static readonly DateTimeOffset Epoch = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
private static Channel NewChannel() => Channel.Create("c", "c", Epoch);
[Fact]
public void OneTime_Covers_OnlyInsideWindow()
{
var ovr = NewChannel()
.AddOverride(OverrideMode.Exclusive, Epoch.AddHours(1), Epoch.AddHours(3));
Assert.False(ovr.Covers(Epoch.AddMinutes(59)));
Assert.True(ovr.Covers(Epoch.AddHours(1)));
Assert.True(ovr.Covers(Epoch.AddHours(2)));
Assert.False(ovr.Covers(Epoch.AddHours(3))); // конец исключается
}
[Fact]
public void AddShow_AttachesWithWeight()
{
var ovr = NewChannel().AddOverride(OverrideMode.Boost, Epoch, Epoch.AddHours(1));
var showId = Guid.NewGuid();
var os = ovr.AddShow(showId, 4);
Assert.Equal(showId, os.ShowId);
Assert.Equal(4, os.Weight);
Assert.Single(ovr.Shows);
}
[Fact]
public void Weekly_Covers_MatchingDayAndMinuteWindow()
{
// Epoch = 2026-01-01 — четверг (DayOfWeek=4).
var day = (int)Epoch.UtcDateTime.DayOfWeek;
var ovr = NewChannel().AddWeeklyOverride(OverrideMode.Exclusive, day, 600, 720); // 10:0012:00
Assert.True(ovr.Covers(Epoch.AddMinutes(600))); // 10:00 того же дня
Assert.True(ovr.Covers(Epoch.AddMinutes(719))); // 11:59
Assert.False(ovr.Covers(Epoch.AddMinutes(720))); // 12:00 — конец исключается
Assert.False(ovr.Covers(Epoch.AddMinutes(599))); // 09:59
}
[Fact]
public void Weekly_Covers_NextWeekSameDay()
{
var day = (int)Epoch.UtcDateTime.DayOfWeek;
var ovr = NewChannel().AddWeeklyOverride(OverrideMode.Exclusive, day, 0, 1440);
Assert.True(ovr.Covers(Epoch.AddDays(7).AddHours(5)));
Assert.False(ovr.Covers(Epoch.AddDays(1).AddHours(5))); // другой день недели
}
}
@@ -0,0 +1,54 @@
using TeleWave.Domain.Broadcast;
using Xunit;
namespace TeleWave.Domain.Tests.Broadcast;
public class ScheduleEntryTests
{
private static readonly DateTimeOffset Start = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
[Fact]
public void Program_SetsKindAndEpg()
{
var channel = Guid.NewGuid();
var asset = Guid.NewGuid();
var show = Guid.NewGuid();
var e = ScheduleEntry.Program(channel, asset, Start, Start.AddMinutes(20), show, 3);
Assert.Equal(ScheduleEntryKind.Program, e.Kind);
Assert.Equal(show, e.ShowId);
Assert.Equal(3, e.EpisodeIndex);
Assert.Null(e.BumperVariantId);
}
[Fact]
public void Ad_HasNoShow()
{
var e = ScheduleEntry.Ad(Guid.NewGuid(), Guid.NewGuid(), Start, Start.AddMinutes(1));
Assert.Equal(ScheduleEntryKind.Ad, e.Kind);
Assert.Null(e.ShowId);
Assert.Null(e.EpisodeIndex);
}
[Fact]
public void Bumper_CarriesShowAndVariant()
{
var toShow = Guid.NewGuid();
var variant = Guid.NewGuid();
var e = ScheduleEntry.Bumper(
Guid.NewGuid(),
Guid.NewGuid(),
Start,
Start.AddSeconds(8),
toShow,
variant
);
Assert.Equal(ScheduleEntryKind.Bumper, e.Kind);
Assert.Equal(toShow, e.ShowId);
Assert.Equal(variant, e.BumperVariantId);
}
}