Files
TeleWave/backend/tests/TeleWave.Domain.Tests/Broadcast/LiveWindowCalculatorTests.cs
T

104 lines
3.5 KiB
C#

using TeleWave.Domain.Broadcast.Live;
using Xunit;
namespace TeleWave.Domain.Tests.Broadcast;
public class LiveWindowCalculatorTests
{
private static readonly DateTimeOffset Epoch = new(2026, 1, 1, 0, 0, 0, TimeSpan.Zero);
private const int Seg = 2;
private const int Window = 5;
private static LiveInput Input(
double nowOffsetSeconds,
IReadOnlyList<LiveEntry> entries,
LiveFiller? filler = null
) =>
new(
Now: Epoch.AddSeconds(nowOffsetSeconds),
Epoch: Epoch,
SegmentSeconds: Seg,
WindowSegments: Window,
Entries: entries,
Filler: filler
);
[Fact]
public void SingleEntry_ProducesWindowWithMonotonicMediaSequence()
{
var asset = Guid.NewGuid();
var entries = new List<LiveEntry> { new(Epoch, Epoch.AddSeconds(100), asset, 50) };
// now = epoch + 20s → currentIndex = 10, окно [6..10].
var playlist = LiveWindowCalculator.Build(Input(20, entries));
Assert.Equal(6, playlist.MediaSequence);
Assert.Equal(Seg, playlist.TargetDuration);
Assert.Equal(5, playlist.Segments.Count);
Assert.Equal([6, 7, 8, 9, 10], playlist.Segments.Select(s => s.LocalIndex));
Assert.All(playlist.Segments, s => Assert.Equal(asset, s.AssetId));
Assert.DoesNotContain(playlist.Segments, s => s.Discontinuity);
}
[Fact]
public void EntryBoundary_InsertsDiscontinuity()
{
Guid a = Guid.NewGuid(),
b = Guid.NewGuid();
var entries = new List<LiveEntry>
{
new(Epoch, Epoch.AddSeconds(16), a, 8), // g0..7
new(Epoch.AddSeconds(16), Epoch.AddSeconds(40), b, 12), // g8..
};
var playlist = LiveWindowCalculator.Build(Input(20, entries));
Assert.Equal(6, playlist.MediaSequence);
Assert.Equal([a, a, b, b, b], playlist.Segments.Select(s => s.AssetId));
// DISCONTINUITY только перед первым сегментом B (индекс 2 в окне).
Assert.Equal(
[false, false, true, false, false],
playlist.Segments.Select(s => s.Discontinuity)
);
}
[Fact]
public void Gap_WithFiller_LoopsFiller()
{
var filler = new LiveFiller(Guid.NewGuid(), 4);
var playlist = LiveWindowCalculator.Build(Input(20, [], filler));
Assert.Equal(6, playlist.MediaSequence);
Assert.Equal(5, playlist.Segments.Count);
Assert.Equal([2, 3, 0, 1, 2], playlist.Segments.Select(s => s.LocalIndex));
Assert.All(playlist.Segments, s => Assert.Equal(filler.AssetId, s.AssetId));
}
[Fact]
public void LeadingGap_WithoutFiller_TrimmedFromWindowStart()
{
var b = Guid.NewGuid();
var entries = new List<LiveEntry>
{
new(Epoch.AddSeconds(16), Epoch.AddSeconds(40), b, 12),
};
// Окно [6..10]: g6,g7 без записи и без филлера → отброшены, старт с g8.
var playlist = LiveWindowCalculator.Build(Input(20, entries));
Assert.Equal(8, playlist.MediaSequence);
Assert.Equal(3, playlist.Segments.Count);
Assert.Equal([0, 1, 2], playlist.Segments.Select(s => s.LocalIndex));
Assert.DoesNotContain(playlist.Segments, s => s.Discontinuity);
}
[Fact]
public void BeforeEpoch_ReturnsEmpty()
{
var playlist = LiveWindowCalculator.Build(Input(-10, []));
Assert.Empty(playlist.Segments);
}
}