Migrate remaining legacy operator workflows
This commit is contained in:
@@ -18,6 +18,7 @@ public sealed class PlayoutOptionsLoaderTests
|
||||
"MBN_STOCK_PLAYOUT_LEGACY_BACKGROUND_ASSET",
|
||||
"MBN_STOCK_PLAYOUT_LEGACY_BACKGROUND_VIDEO_LOOP_COUNT",
|
||||
"MBN_STOCK_PLAYOUT_LEGACY_BACKGROUND_VIDEO_LOOP_INFINITE",
|
||||
"MBN_STOCK_PLAYOUT_LEGACY_BACKGROUND_DIRECTORY",
|
||||
"MBN_STOCK_PLAYOUT_SCENE_DIRECTORY",
|
||||
"MBN_STOCK_PLAYOUT_TEST_WINDOW_TITLE_PATTERN",
|
||||
"MBN_STOCK_PLAYOUT_QUEUE_CAPACITY",
|
||||
@@ -53,6 +54,7 @@ public sealed class PlayoutOptionsLoaderTests
|
||||
Assert.Null(options.LegacySceneBackgroundAssetPath);
|
||||
Assert.Equal(2004, options.LegacySceneBackgroundVideoLoopCount);
|
||||
Assert.True(options.LegacySceneBackgroundVideoLoopInfinite);
|
||||
Assert.Null(options.LegacyBackgroundDirectory);
|
||||
Assert.Empty(options.TestSceneAllowlist);
|
||||
Assert.False(options.TrustedLiveOutputEnabled);
|
||||
Assert.True(options.ReconnectEnabled);
|
||||
@@ -74,6 +76,7 @@ public sealed class PlayoutOptionsLoaderTests
|
||||
.Set("MBN_STOCK_PLAYOUT_LEGACY_BACKGROUND_ASSET", "Video\\studio.vrv")
|
||||
.Set("MBN_STOCK_PLAYOUT_LEGACY_BACKGROUND_VIDEO_LOOP_COUNT", "2004")
|
||||
.Set("MBN_STOCK_PLAYOUT_LEGACY_BACKGROUND_VIDEO_LOOP_INFINITE", "false")
|
||||
.Set("MBN_STOCK_PLAYOUT_LEGACY_BACKGROUND_DIRECTORY", "C:\\env-backgrounds")
|
||||
.Set("MBN_STOCK_PLAYOUT_SCENE_DIRECTORY", "C:\\env-scenes")
|
||||
.Set("MBN_STOCK_PLAYOUT_TEST_WINDOW_TITLE_PATTERN", "ENV TEST")
|
||||
.Set("MBN_STOCK_PLAYOUT_QUEUE_CAPACITY", "14")
|
||||
@@ -125,6 +128,7 @@ public sealed class PlayoutOptionsLoaderTests
|
||||
Assert.Equal("Video\\studio.vrv", options.LegacySceneBackgroundAssetPath);
|
||||
Assert.Equal(2004, options.LegacySceneBackgroundVideoLoopCount);
|
||||
Assert.False(options.LegacySceneBackgroundVideoLoopInfinite);
|
||||
Assert.Equal("C:\\env-backgrounds", options.LegacyBackgroundDirectory);
|
||||
Assert.Equal("C:\\env-scenes", options.SceneDirectory);
|
||||
Assert.Equal("ENV TEST", options.TestProcessWindowTitlePattern);
|
||||
Assert.Equal(14, options.QueueCapacity);
|
||||
|
||||
@@ -368,6 +368,85 @@ public sealed class PlayoutSafetyValidationTests : IDisposable
|
||||
ignoreCase: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveCue_OperatorBackgroundUsesOnlyTheSeparateTrustedRoot()
|
||||
{
|
||||
using var backgrounds = TemporarySceneDirectory.Create("studio.vrv");
|
||||
var options = ValidTestOptions();
|
||||
options.LegacyBackgroundDirectory = backgrounds.Path;
|
||||
var validated = ValidatedPlayoutOptions.Create(options);
|
||||
|
||||
var resolved = validated.ResolveCue(new PlayoutCue(
|
||||
"test-scene.t2s",
|
||||
"test-scene",
|
||||
Mutations:
|
||||
[
|
||||
new PlayoutSetBackgroundVideo(
|
||||
"studio.vrv",
|
||||
LoopCount: 2004,
|
||||
LoopInfinite: true,
|
||||
AssetRoot: PlayoutAssetRoot.OperatorBackgroundDirectory)
|
||||
]));
|
||||
|
||||
var video = Assert.IsType<PlayoutSetBackgroundVideo>(Assert.Single(resolved.Mutations!));
|
||||
Assert.Equal(
|
||||
Path.Combine(backgrounds.Path, "studio.vrv"),
|
||||
video.AssetPath,
|
||||
ignoreCase: true);
|
||||
Assert.Equal(PlayoutAssetRoot.OperatorBackgroundDirectory, video.AssetRoot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveCue_OperatorBackgroundDoesNotFallBackToCutsRoot()
|
||||
{
|
||||
using var backgrounds = TemporarySceneDirectory.Create();
|
||||
var options = ValidTestOptions();
|
||||
options.LegacyBackgroundDirectory = backgrounds.Path;
|
||||
var validated = ValidatedPlayoutOptions.Create(options);
|
||||
|
||||
Assert.Throws<PlayoutRequestException>(() => validated.ResolveCue(new PlayoutCue(
|
||||
"test-scene.t2s",
|
||||
"test-scene",
|
||||
Mutations:
|
||||
[
|
||||
new PlayoutSetBackgroundVideo(
|
||||
"Video\\background.vrv",
|
||||
LoopCount: 2004,
|
||||
LoopInfinite: true,
|
||||
AssetRoot: PlayoutAssetRoot.OperatorBackgroundDirectory)
|
||||
])));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveCue_RevalidatesConfiguredBackgroundRootAtCommandTime()
|
||||
{
|
||||
var lateRoot = Path.Combine(Directory.GetParent(_scenes.Path)!.FullName, "late-background");
|
||||
var options = ValidTestOptions();
|
||||
options.LegacyBackgroundDirectory = lateRoot;
|
||||
var validated = ValidatedPlayoutOptions.Create(options);
|
||||
|
||||
Directory.CreateDirectory(lateRoot);
|
||||
File.WriteAllText(Path.Combine(lateRoot, "studio.vrv"), "late trusted background");
|
||||
|
||||
var resolved = validated.ResolveCue(new PlayoutCue(
|
||||
"test-scene.t2s",
|
||||
"test-scene",
|
||||
Mutations:
|
||||
[
|
||||
new PlayoutSetBackgroundVideo(
|
||||
"studio.vrv",
|
||||
LoopCount: 2004,
|
||||
LoopInfinite: true,
|
||||
AssetRoot: PlayoutAssetRoot.OperatorBackgroundDirectory)
|
||||
]));
|
||||
|
||||
var video = Assert.IsType<PlayoutSetBackgroundVideo>(Assert.Single(resolved.Mutations!));
|
||||
Assert.Equal(
|
||||
Path.Combine(lateRoot, "studio.vrv"),
|
||||
video.AssetPath,
|
||||
ignoreCase: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveCue_AcceptsTheLegacy2004BackgroundVideoLoopValue()
|
||||
{
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using MBN_STOCK_WEBVIEW.Core.Playout;
|
||||
using MBN_STOCK_WEBVIEW.Core.Playout.Scenes;
|
||||
|
||||
namespace MBN_STOCK_WEBVIEW.Playout.Tests;
|
||||
@@ -17,18 +18,24 @@ public sealed class PlayoutSceneCompositionFactoryTests
|
||||
[Fact]
|
||||
public void TrustedRelativeVideo_IsVerifiedEvenInDryRun()
|
||||
{
|
||||
using var scenes = TemporarySceneDirectory.Create("Video\\studio.vrv");
|
||||
using var scenes = TemporarySceneDirectory.Create("5001.t2s");
|
||||
var backgroundRoot = Path.Combine(Directory.GetParent(scenes.Path)!.FullName, "배경");
|
||||
Directory.CreateDirectory(backgroundRoot);
|
||||
File.WriteAllText(Path.Combine(backgroundRoot, "studio.vrv"), "trusted background");
|
||||
var result = PlayoutSceneCompositionFactory.Create(new PlayoutOptions
|
||||
{
|
||||
Mode = PlayoutMode.DryRun,
|
||||
SceneDirectory = scenes.Path,
|
||||
LegacySceneBackgroundKind = LegacySceneBackgroundKind.Video,
|
||||
LegacySceneBackgroundAssetPath = "Video\\studio.vrv"
|
||||
LegacySceneBackgroundAssetPath = "studio.vrv"
|
||||
});
|
||||
|
||||
Assert.Equal(LegacySceneBackgroundKind.Video, result.BackgroundKind);
|
||||
Assert.Equal(Path.Combine("Video", "studio.vrv"), result.BackgroundAssetPath);
|
||||
Assert.Equal("studio.vrv", result.BackgroundAssetPath);
|
||||
Assert.Equal(2004, result.BackgroundVideoLoopCount);
|
||||
Assert.Equal(
|
||||
PlayoutAssetRoot.OperatorBackgroundDirectory,
|
||||
result.BackgroundAssetRoot);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@@ -38,7 +45,13 @@ public sealed class PlayoutSceneCompositionFactoryTests
|
||||
[InlineData("Video\\studio.exe")]
|
||||
public void InvalidOrMissingBackground_FailsBeforeDryRunPrepare(string relativePath)
|
||||
{
|
||||
using var scenes = TemporarySceneDirectory.Create("Video\\studio.vrv");
|
||||
using var scenes = TemporarySceneDirectory.Create("5001.t2s");
|
||||
var backgroundRoot = Path.Combine(Directory.GetParent(scenes.Path)!.FullName, "배경");
|
||||
Directory.CreateDirectory(backgroundRoot);
|
||||
Directory.CreateDirectory(Path.Combine(backgroundRoot, "Video"));
|
||||
File.WriteAllText(
|
||||
Path.Combine(backgroundRoot, "Video", "studio.vrv"),
|
||||
"trusted background");
|
||||
var options = new PlayoutOptions
|
||||
{
|
||||
Mode = PlayoutMode.DryRun,
|
||||
@@ -50,4 +63,33 @@ public sealed class PlayoutSceneCompositionFactoryTests
|
||||
Assert.Throws<PlayoutConfigurationException>(() =>
|
||||
PlayoutSceneCompositionFactory.Create(options));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LegacyDefault_UsesSiblingBackgroundRoot_NotTheCutsRoot()
|
||||
{
|
||||
using var scenes = TemporarySceneDirectory.Create("기본.vrv");
|
||||
var backgroundRoot = Path.Combine(Directory.GetParent(scenes.Path)!.FullName, "배경");
|
||||
Directory.CreateDirectory(backgroundRoot);
|
||||
File.WriteAllText(
|
||||
Path.Combine(backgroundRoot, PlayoutSceneCompositionFactory.LegacyDefaultBackgroundFileName),
|
||||
"trusted background");
|
||||
|
||||
var result = PlayoutSceneCompositionFactory.CreateLegacyDefault(
|
||||
new PlayoutOptions { SceneDirectory = scenes.Path },
|
||||
fadeDuration: 6);
|
||||
|
||||
Assert.Equal("기본.vrv", result.BackgroundAssetPath);
|
||||
Assert.Equal(PlayoutAssetRoot.OperatorBackgroundDirectory, result.BackgroundAssetRoot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LegacyDefault_MissingSiblingDirectory_FailsClosed()
|
||||
{
|
||||
using var scenes = TemporarySceneDirectory.Create("기본.vrv");
|
||||
|
||||
Assert.Throws<PlayoutConfigurationException>(() =>
|
||||
PlayoutSceneCompositionFactory.CreateLegacyDefault(
|
||||
new PlayoutOptions { SceneDirectory = scenes.Path },
|
||||
fadeDuration: 6));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace MBN_STOCK_WEBVIEW.Playout.Tests;
|
||||
|
||||
public sealed class TrustedPlayoutAssetPathTests
|
||||
{
|
||||
private static readonly IReadOnlySet<string> Allowed = new HashSet<string>(
|
||||
[".vrv", ".png"],
|
||||
StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
[Fact]
|
||||
public void ResolveRelativeFile_AcceptsOneInternalRegularFile()
|
||||
{
|
||||
using var root = TemporarySceneDirectory.Create("studio.vrv");
|
||||
|
||||
var resolved = TrustedPlayoutAssetPath.ResolveRelativeFile(
|
||||
root.Path,
|
||||
"studio.vrv",
|
||||
Allowed);
|
||||
|
||||
Assert.Equal(Path.Combine(root.Path, "studio.vrv"), resolved, ignoreCase: true);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("..\\outside.vrv")]
|
||||
[InlineData("C:\\outside.vrv")]
|
||||
public void ResolveRelativeFile_RejectsRootEscape(string input)
|
||||
{
|
||||
using var root = TemporarySceneDirectory.Create("studio.vrv");
|
||||
|
||||
Assert.Throws<TrustedPlayoutAssetException>(() =>
|
||||
TrustedPlayoutAssetPath.ResolveRelativeFile(root.Path, input, Allowed));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveRelativeFile_RejectsMissingFile()
|
||||
{
|
||||
using var root = TemporarySceneDirectory.Create();
|
||||
|
||||
Assert.Throws<TrustedPlayoutAssetException>(() =>
|
||||
TrustedPlayoutAssetPath.ResolveRelativeFile(root.Path, "missing.vrv", Allowed));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveAbsoluteFile_RejectsPickerSelectionOutsideTheRoot()
|
||||
{
|
||||
using var root = TemporarySceneDirectory.Create();
|
||||
using var outside = TemporarySceneDirectory.Create("outside.vrv");
|
||||
|
||||
Assert.Throws<TrustedPlayoutAssetException>(() =>
|
||||
TrustedPlayoutAssetPath.ResolveAbsoluteFile(
|
||||
root.Path,
|
||||
Path.Combine(outside.Path, "outside.vrv"),
|
||||
Allowed));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveRelativeFile_RejectsHardLinkedFile()
|
||||
{
|
||||
using var root = TemporarySceneDirectory.Create();
|
||||
var parent = Directory.GetParent(root.Path)!.FullName;
|
||||
var outside = Path.Combine(parent, "outside.vrv");
|
||||
File.WriteAllText(outside, "outside trusted-looking bytes");
|
||||
var link = Path.Combine(root.Path, "linked.vrv");
|
||||
Assert.True(CreateHardLinkW(link, outside, IntPtr.Zero));
|
||||
|
||||
Assert.Throws<TrustedPlayoutAssetException>(() =>
|
||||
TrustedPlayoutAssetPath.ResolveRelativeFile(root.Path, "linked.vrv", Allowed));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ResolveRelativeFile_RejectsReparseDirectory()
|
||||
{
|
||||
using var root = TemporarySceneDirectory.Create();
|
||||
var parent = Directory.GetParent(root.Path)!.FullName;
|
||||
var outside = Path.Combine(parent, "outside");
|
||||
Directory.CreateDirectory(outside);
|
||||
File.WriteAllText(Path.Combine(outside, "studio.vrv"), "outside bytes");
|
||||
var link = Path.Combine(root.Path, "linked");
|
||||
try
|
||||
{
|
||||
Directory.CreateSymbolicLink(link, outside);
|
||||
}
|
||||
catch (Exception exception) when (
|
||||
exception is UnauthorizedAccessException or IOException or PlatformNotSupportedException)
|
||||
{
|
||||
throw Xunit.Sdk.SkipException.ForSkip(
|
||||
"The Windows test host does not permit creating a symbolic link.");
|
||||
}
|
||||
|
||||
Assert.Throws<TrustedPlayoutAssetException>(() =>
|
||||
TrustedPlayoutAssetPath.ResolveRelativeFile(
|
||||
root.Path,
|
||||
"linked\\studio.vrv",
|
||||
Allowed));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void EnsureOpenedRegularFile_BindsTheHandleToTheExpectedPath()
|
||||
{
|
||||
using var root = TemporarySceneDirectory.Create("expected.vrv", "other.vrv");
|
||||
var expected = Path.Combine(root.Path, "expected.vrv");
|
||||
var other = Path.Combine(root.Path, "other.vrv");
|
||||
using var stream = new FileStream(
|
||||
other,
|
||||
FileMode.Open,
|
||||
FileAccess.Read,
|
||||
FileShare.Read);
|
||||
|
||||
Assert.Throws<TrustedPlayoutAssetException>(() =>
|
||||
TrustedPlayoutAssetPath.EnsureOpenedRegularFile(
|
||||
stream.SafeFileHandle,
|
||||
expected));
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
|
||||
[return: MarshalAs(UnmanagedType.Bool)]
|
||||
private static extern bool CreateHardLinkW(
|
||||
string fileName,
|
||||
string existingFileName,
|
||||
IntPtr securityAttributes);
|
||||
}
|
||||
Reference in New Issue
Block a user