111 lines
4.4 KiB
C#
111 lines
4.4 KiB
C#
using LiveRecorder.Domain.Entities;
|
|
using LiveRecorder.Domain.Enums;
|
|
using LiveRecorder.Infrastructure.Services;
|
|
using System.Xml.Linq;
|
|
|
|
namespace LiveRecorder.Tests;
|
|
|
|
public sealed class RecordingContinuityTests
|
|
{
|
|
[Theory]
|
|
[InlineData(true, false, true)]
|
|
[InlineData(false, true, true)]
|
|
[InlineData(false, false, false)]
|
|
public void SessionTransition_IsTreatedAsActive(bool process, bool transition, bool expected) =>
|
|
Assert.Equal(expected, FfmpegService.IsSessionRuntimeActive(process, transition));
|
|
|
|
[Theory]
|
|
[InlineData(1, 5)]
|
|
[InlineData(2, 15)]
|
|
[InlineData(3, 30)]
|
|
[InlineData(4, 60)]
|
|
[InlineData(5, 120)]
|
|
[InlineData(50, 120)]
|
|
public void RuntimeRecoveryBackoff_IsBoundedAtTwoMinutes(int attempt, int seconds) =>
|
|
Assert.Equal(TimeSpan.FromSeconds(seconds), FfmpegService.GetRuntimeRecoveryDelay(attempt));
|
|
|
|
[Theory]
|
|
[InlineData(null, false, false)]
|
|
[InlineData(0.0, false, false)]
|
|
[InlineData(0.1, true, false)]
|
|
[InlineData(4.99, true, false)]
|
|
[InlineData(5.0, true, true)]
|
|
public void MediaDuration_SeparatesReadableFromStandalone(double? duration, bool readable, bool standalone)
|
|
{
|
|
Assert.Equal(readable, FfmpegService.IsReadableMediaDuration(duration));
|
|
Assert.Equal(standalone, FfmpegService.IsStandaloneMediaDuration(duration));
|
|
}
|
|
|
|
[Fact]
|
|
public void OfflineSample_RequiresConfirmation_ButLiveDoesNot()
|
|
{
|
|
Assert.True(LiveRoomPollingBackgroundService.RequiresOfflineConfirmation(false));
|
|
Assert.False(LiveRoomPollingBackgroundService.RequiresOfflineConfirmation(true));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(false, RecordTaskStatus.Starting, true)]
|
|
[InlineData(false, RecordTaskStatus.Running, true)]
|
|
[InlineData(true, RecordTaskStatus.Running, false)]
|
|
[InlineData(false, RecordTaskStatus.Completed, false)]
|
|
public void ArtifactlessRecovery_ReusesOnlyActiveTask(bool hasMedia, RecordTaskStatus status, bool expected) =>
|
|
Assert.Equal(expected, FfmpegService.ShouldReuseRecoveryTask(hasMedia, status));
|
|
|
|
[Fact]
|
|
public void SessionRecoveryMarker_KeepsSessionActive()
|
|
{
|
|
var session = new RecordSession(Guid.NewGuid(), "origin", RecordOutputFormat.Mp4, RecordSaveMode.Segmented, DateTimeOffset.UtcNow);
|
|
session.MarkRecovering("[runtime-recovery] attempt=4; curl EOF", DateTimeOffset.UtcNow);
|
|
|
|
Assert.Equal(RecordSessionStatus.Starting, session.Status);
|
|
Assert.StartsWith("[runtime-recovery]", session.ErrorMessage);
|
|
Assert.Null(session.EndedAt);
|
|
}
|
|
|
|
[Fact]
|
|
public void MergedSource_PointsAtTargetAndBecomesHidden()
|
|
{
|
|
var source = new RecordTask(Guid.NewGuid(), Guid.NewGuid(), 2, "origin", RecordOutputFormat.Mp4, DateTimeOffset.UtcNow);
|
|
var targetId = Guid.NewGuid();
|
|
source.MarkMergedSource(targetId, DateTimeOffset.UtcNow);
|
|
|
|
Assert.True(source.IsHiddenArtifactSource);
|
|
Assert.Equal(targetId, source.MergedIntoRecordTaskId);
|
|
}
|
|
|
|
[Fact]
|
|
public void MergeWindow_FirstShortFragment_MergesForward()
|
|
{
|
|
var window = ShortFragmentConsolidationService.ResolveMergeWindow([1.2, 60], 0);
|
|
Assert.Equal((0, 1), window);
|
|
}
|
|
|
|
[Fact]
|
|
public void MergeWindow_TrailingShortFragment_MergesBackward()
|
|
{
|
|
var window = ShortFragmentConsolidationService.ResolveMergeWindow([60, 1.2], 0);
|
|
Assert.Equal((0, 1), window);
|
|
}
|
|
|
|
[Fact]
|
|
public void MergeWindow_ConsecutiveShortFragments_AreAccumulated()
|
|
{
|
|
var window = ShortFragmentConsolidationService.ResolveMergeWindow([1.2, 2.4, 60], 1);
|
|
Assert.Equal((0, 2), window);
|
|
}
|
|
|
|
[Fact]
|
|
public void DanmakuMerge_ShiftsRelativeOffset_ButPreservesAbsoluteTimestamp()
|
|
{
|
|
var chat = XElement.Parse("<d p=\"1.5,1,25,16777215,1720000000000,0,1,0\">hi</d>");
|
|
var shiftedChat = ShortFragmentConsolidationService.AdjustDanmakuElementOffsets(chat, 4.25);
|
|
Assert.StartsWith("5.8,", shiftedChat.Attribute("p")!.Value);
|
|
Assert.Contains("1720000000000", shiftedChat.Attribute("p")!.Value);
|
|
|
|
var eventElement = XElement.Parse("<event ts=\"1720000000123\" offset=\"2.0\" />");
|
|
var shiftedEvent = ShortFragmentConsolidationService.AdjustDanmakuElementOffsets(eventElement, 4.25);
|
|
Assert.Equal("6.2", shiftedEvent.Attribute("offset")!.Value);
|
|
Assert.Equal("1720000000123", shiftedEvent.Attribute("ts")!.Value);
|
|
}
|
|
}
|