Files
MBN_STOCK_WEBVIEW/tests/MBN_STOCK_WEBVIEW.Playout.Tests/DryRunPlayoutEngineTests.cs

186 lines
7.4 KiB
C#

using MBN_STOCK_WEBVIEW.Playout.Runtime;
namespace MBN_STOCK_WEBVIEW.Playout.Tests;
public sealed class DryRunPlayoutEngineTests
{
[Fact]
public async Task DefaultOptions_AreDryRunAndNeverReportARealConnection()
{
await using var engine = CreateEngine(PlayoutMode.DryRun);
var result = await engine.ConnectAsync(CancellationToken.None);
Assert.Equal(PlayoutMode.DryRun, engine.Status.Mode);
Assert.Equal(PlayoutConnectionState.DryRunReady, engine.Status.State);
Assert.True(engine.Status.IsCommandAvailable);
Assert.False(engine.Status.IsConnected);
Assert.Equal(
PlayoutKtapConnectState.NotAttempted,
engine.Status.LastKtapConnectState);
Assert.False(engine.Status.KtapConnectAttempted);
Assert.False(engine.Status.NetworkMonitoringRecordExpected);
Assert.False(engine.Status.NetworkMonitoringCheckRequired);
Assert.Null(engine.Status.KtapHelloObserved);
Assert.Contains("Network Monitoring", engine.Status.Message, StringComparison.Ordinal);
Assert.True(result.IsSuccess);
Assert.True(result.IsDryRun);
}
[Fact]
public async Task DisabledMode_RejectsEveryOperationWithoutChangingState()
{
await using var engine = CreateEngine(PlayoutMode.Disabled);
var cue = Cue("disabled-scene");
var results = new[]
{
await engine.ConnectAsync(CancellationToken.None),
await engine.PrepareAsync(cue, CancellationToken.None),
await engine.TakeInAsync(CancellationToken.None),
await engine.NextAsync(cue, CancellationToken.None),
await engine.TakeOutAsync(
PlayoutTakeOutScope.All,
CancellationToken.None),
await engine.DisconnectAsync(CancellationToken.None)
};
Assert.All(results, result => Assert.Equal(PlayoutResultCode.Rejected, result.Code));
Assert.All(results, result => Assert.False(result.IsDryRun));
Assert.Equal(PlayoutConnectionState.Disabled, engine.Status.State);
Assert.False(engine.Status.IsConnected);
Assert.False(engine.Status.IsCommandAvailable);
Assert.Null(engine.Status.PreparedSceneName);
Assert.Null(engine.Status.OnAirSceneName);
}
[Fact]
public async Task DryRun_TransitionsPreparedAndOnAirSceneStateInCommandOrder()
{
await using var engine = CreateEngine(PlayoutMode.DryRun);
var statuses = new List<PlayoutStatus>();
engine.StatusChanged += (_, args) => statuses.Add(args.Current);
var connect = await engine.ConnectAsync(CancellationToken.None);
var prepare = await engine.PrepareAsync(
Cue("first-scene"),
CancellationToken.None);
Assert.Equal("first-scene", engine.Status.PreparedSceneName);
Assert.Null(engine.Status.OnAirSceneName);
var takeIn = await engine.TakeInAsync(CancellationToken.None);
Assert.Null(engine.Status.PreparedSceneName);
Assert.Equal("first-scene", engine.Status.OnAirSceneName);
var next = await engine.NextAsync(
Cue("next-scene"),
CancellationToken.None);
Assert.Null(engine.Status.PreparedSceneName);
Assert.Equal("next-scene", engine.Status.OnAirSceneName);
var takeOut = await engine.TakeOutAsync(
PlayoutTakeOutScope.Layout,
CancellationToken.None);
Assert.Null(engine.Status.OnAirSceneName);
var disconnect = await engine.DisconnectAsync(CancellationToken.None);
Assert.All(
new[] { connect, prepare, takeIn, next, takeOut, disconnect },
result => Assert.Equal(PlayoutResultCode.Success, result.Code));
Assert.All(
new[] { connect, prepare, takeIn, next, takeOut, disconnect },
result => Assert.True(result.IsDryRun));
Assert.Equal(6, statuses.Count);
Assert.True(statuses.Zip(statuses.Skip(1), (left, right) => left.Sequence < right.Sequence).All(x => x));
Assert.All(statuses, status => Assert.Equal(PlayoutConnectionState.DryRunReady, status.State));
}
[Fact]
public async Task DryRun_TakeInWithoutPreparedSceneIsRejected()
{
await using var engine = CreateEngine(PlayoutMode.DryRun);
var result = await engine.TakeInAsync(CancellationToken.None);
Assert.Equal(PlayoutResultCode.Rejected, result.Code);
Assert.Null(engine.Status.OnAirSceneName);
}
[Fact]
public async Task DryRun_NextAfterPrepareButBeforeTakeInIsRejected()
{
await using var engine = CreateEngine(PlayoutMode.DryRun);
var idleNext = await engine.NextAsync(
Cue("next-scene"),
CancellationToken.None);
Assert.Equal(PlayoutResultCode.Rejected, idleNext.Code);
Assert.Null(engine.Status.PreparedSceneName);
Assert.Null(engine.Status.OnAirSceneName);
Assert.True((await engine.PrepareAsync(
Cue("first-scene"),
CancellationToken.None)).IsSuccess);
var next = await engine.NextAsync(
Cue("next-scene"),
CancellationToken.None);
Assert.Equal(PlayoutResultCode.Rejected, next.Code);
Assert.Equal("first-scene", engine.Status.PreparedSceneName);
Assert.Null(engine.Status.OnAirSceneName);
}
[Fact]
public async Task CancelledDryRunCommand_DoesNotChangeStatus()
{
await using var engine = CreateEngine(PlayoutMode.DryRun);
var initialStatus = engine.Status;
using var cancellation = new CancellationTokenSource();
cancellation.Cancel();
var result = await engine.PrepareAsync(Cue("cancelled-scene"), cancellation.Token);
Assert.Equal(PlayoutResultCode.Cancelled, result.Code);
Assert.Same(initialStatus, engine.Status);
Assert.Null(engine.Status.PreparedSceneName);
}
[Fact]
public async Task DryRun_PublicStatusAndResultDoNotExposeScenePath()
{
const string secretPath = "C:\\private-vendor-assets\\do-not-log\\secret.t2s";
await using var engine = CreateEngine(PlayoutMode.DryRun);
var cue = new PlayoutCue(secretPath, "C:\\unsafe-scene-name");
var result = await engine.PrepareAsync(cue, CancellationToken.None);
Assert.True(result.IsSuccess);
Assert.Equal("장면", engine.Status.PreparedSceneName);
Assert.DoesNotContain(secretPath, result.Message, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain(secretPath, engine.Status.Message, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain(cue.SceneName, result.Message, StringComparison.OrdinalIgnoreCase);
Assert.DoesNotContain(cue.SceneName, engine.Status.Message, StringComparison.OrdinalIgnoreCase);
}
private static PlayoutCue Cue(string sceneName) => new(
$"C:\\test-only\\{sceneName}.t2s",
sceneName,
[new PlayoutField("headline", "safe value", true)]);
private static TornadoPlayoutEngine CreateEngine(PlayoutMode mode)
{
var options = ValidatedPlayoutOptions.Create(new PlayoutOptions { Mode = mode });
return new TornadoPlayoutEngine(
options,
new FakeK3dSessionFactory(),
new FakeRegistrationProbe(),
new FakeTornadoProcessProbe(new TornadoProcessSnapshot(0, 0, 0)),
null,
new FakeLiveAuthorization(false),
TimeProvider.System,
startMonitor: false);
}
}