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

370 lines
12 KiB
C#

using MBN_STOCK_WEBVIEW.PlayoutSmoke;
namespace MBN_STOCK_WEBVIEW.Playout.Tests;
public sealed class SmokeCommandLineTests
{
[Theory]
[InlineData("Tornado2 TEST", false)]
[InlineData("TEST-1", false)]
[InlineData("Tornado2", true)]
[InlineData("PGM", true)]
[InlineData("CONTEST", true)]
[InlineData("", true)]
[InlineData(null, true)]
public void TornadoWindowTitleWithoutExplicitTestMarkerIsUnsafe(
string? title,
bool expected)
{
Assert.Equal(expected, SmokeCommandLine.IsUnsafeTornadoWindowTitle(title));
}
[Fact]
public void Parse_NoArguments_PreservesReadOnlyProbeDefault()
{
var parsed = SmokeCommandLine.Parse([]);
Assert.True(parsed.IsValid);
Assert.Equal(SmokeCommandKind.Probe, parsed.Invocation.Kind);
}
[Theory]
[InlineData("--probe", "Probe")]
[InlineData("--PROBE", "Probe")]
[InlineData("--dry-run", "DryRun")]
public void Parse_ExistingComFreeCommandsRemainAvailable(
string argument,
string expected)
{
var parsed = SmokeCommandLine.Parse([argument]);
Assert.True(parsed.IsValid);
Assert.Equal(expected, parsed.Invocation.Kind.ToString());
}
[Fact]
public void Parse_TestConnectRequiresExplicitAcknowledgementAndConfigArgument()
{
var withoutAcknowledgement = SmokeCommandLine.Parse(
["--test-connect", "--config", "C:\\safe\\playout.local.json"]);
var complete = SmokeCommandLine.Parse(
[
"--test-connect",
SmokeCommandLine.AcknowledgementFlag,
"--config",
"C:\\safe\\playout.local.json"
]);
Assert.False(withoutAcknowledgement.IsValid);
Assert.Equal("missing-acknowledgement", withoutAcknowledgement.ErrorCode);
Assert.True(complete.IsValid);
Assert.Equal(SmokeCommandKind.TestConnect, complete.Invocation.Kind);
}
[Fact]
public void Parse_TestSequenceRequiresBothSceneCodesAndBoundedObservation()
{
var missingObservation = SmokeCommandLine.Parse(
[
"--test-sequence",
SmokeCommandLine.AcknowledgementFlag,
"--config",
"C:\\safe\\playout.local.json",
"--prepare",
"5001",
"--next",
"5006"
]);
var complete = SmokeCommandLine.Parse(
[
"--test-sequence",
"--prepare",
"5001",
"--observe-ms",
"1000",
SmokeCommandLine.AcknowledgementFlag,
"--next",
"5006",
"--config",
"C:\\safe\\playout.local.json"
]);
Assert.False(missingObservation.IsValid);
Assert.Equal("missing-required-option", missingObservation.ErrorCode);
Assert.True(complete.IsValid);
Assert.Equal(1000, complete.Invocation.ObservationMilliseconds);
}
[Theory]
[InlineData("999")]
[InlineData("30001")]
[InlineData("1.5")]
[InlineData("secret")]
public void Parse_RejectsUnboundedOrNonIntegralObservation(string value)
{
var parsed = ValidSequenceArguments(observation: value);
Assert.False(parsed.IsValid);
Assert.Equal("invalid-observation-duration", parsed.ErrorCode);
}
[Theory]
[InlineData("../5001")]
[InlineData("5001.t2s")]
[InlineData("장면")]
[InlineData("SCENE A")]
public void Parse_RejectsUnsafeSceneCodesWithoutReflectingThem(string sceneCode)
{
var parsed = ValidSequenceArguments(prepare: sceneCode);
Assert.False(parsed.IsValid);
Assert.Equal("unsafe-scene-code", parsed.ErrorCode);
Assert.DoesNotContain(sceneCode, parsed.ErrorCode, StringComparison.OrdinalIgnoreCase);
}
[Fact]
public void Parse_TestConnectRejectsMutationOptions()
{
var parsed = SmokeCommandLine.Parse(
[
"--test-connect",
SmokeCommandLine.AcknowledgementFlag,
"--config",
"C:\\safe\\playout.local.json",
"--prepare",
"5001"
]);
Assert.False(parsed.IsValid);
Assert.Equal("unknown-option", parsed.ErrorCode);
}
[Fact]
public void Parse_TestSequenceRequiresTwoDistinctSceneCodes()
{
var parsed = SmokeCommandLine.Parse(
[
"--test-sequence",
SmokeCommandLine.AcknowledgementFlag,
"--config",
"C:\\safe\\playout.local.json",
"--prepare",
"5001",
"--next",
"5001",
"--observe-ms",
"1000"
]);
Assert.False(parsed.IsValid);
Assert.Equal("duplicate-scene-code", parsed.ErrorCode);
}
[Fact]
public void Parse_TestPlanUsesTheSameSequenceInputsButNeverImpliesExecution()
{
var parsed = SmokeCommandLine.Parse(
[
"--test-plan",
SmokeCommandLine.AcknowledgementFlag,
"--config",
"C:\\safe\\playout.local.json",
"--prepare",
"5001",
"--next",
"5006",
"--observe-ms",
"5000"
]);
Assert.True(parsed.IsValid);
Assert.Equal(SmokeCommandKind.TestPlan, parsed.Invocation.Kind);
}
[Fact]
public void Parse_PgmConnectDiagnosticRequiresDistinctAcknowledgementAndExactInputs()
{
string[] complete =
[
"--pgm-connect-diagnostic",
SmokeCommandLine.PgmConnectAcknowledgementFlag,
"--host",
"127.0.0.1",
"--port",
"30001",
"--expected-pgm-window-title",
"PGM"
];
var missingAcknowledgement = SmokeCommandLine.Parse(complete[0..1]
.Concat(complete[2..])
.ToArray());
var wrongAcknowledgement = SmokeCommandLine.Parse(
complete.Select(value => value == SmokeCommandLine.PgmConnectAcknowledgementFlag
? SmokeCommandLine.AcknowledgementFlag
: value).ToArray());
var parsed = SmokeCommandLine.Parse(complete);
Assert.False(missingAcknowledgement.IsValid);
Assert.Equal("missing-acknowledgement", missingAcknowledgement.ErrorCode);
Assert.False(wrongAcknowledgement.IsValid);
Assert.Equal("unknown-option", wrongAcknowledgement.ErrorCode);
Assert.True(parsed.IsValid);
Assert.Equal(SmokeCommandKind.PgmConnectDiagnostic, parsed.Invocation.Kind);
Assert.Equal("127.0.0.1", parsed.Invocation.Host);
Assert.Equal(30001, parsed.Invocation.Port);
Assert.Equal("PGM", parsed.Invocation.ExpectedPgmWindowTitle);
Assert.Null(parsed.Invocation.ConfigPath);
}
[Theory]
[InlineData("--config")]
[InlineData("--prepare")]
[InlineData("--next")]
[InlineData("--observe-ms")]
public void Parse_PgmConnectDiagnosticRejectsEveryConfigOrRenderOption(string option)
{
var parsed = SmokeCommandLine.Parse(
[
"--pgm-connect-diagnostic",
SmokeCommandLine.PgmConnectAcknowledgementFlag,
"--host",
"127.0.0.1",
"--port",
"30001",
"--expected-pgm-window-title",
"PGM",
option,
"5001"
]);
Assert.False(parsed.IsValid);
Assert.Equal("unknown-option", parsed.ErrorCode);
}
[Theory]
[InlineData("0")]
[InlineData("65536")]
[InlineData("secret")]
public void Parse_PgmConnectDiagnosticRejectsInvalidPort(string port)
{
var parsed = SmokeCommandLine.Parse(
[
"--pgm-connect-diagnostic",
SmokeCommandLine.PgmConnectAcknowledgementFlag,
"--host",
"127.0.0.1",
"--port",
port,
"--expected-pgm-window-title",
"PGM"
]);
Assert.False(parsed.IsValid);
Assert.Equal("invalid-port", parsed.ErrorCode);
}
[Fact]
public void Parse_PgmCutsSequenceRequiresItsOwnLongAcknowledgementAndFixedInputs()
{
var complete = ValidPgmCutsArguments();
var missingAcknowledgement = SmokeCommandLine.Parse(
complete.Where(value => value != SmokeCommandLine.PgmCutsSequenceAcknowledgementFlag)
.ToArray());
var wrongAcknowledgement = SmokeCommandLine.Parse(
complete.Select(value => value == SmokeCommandLine.PgmCutsSequenceAcknowledgementFlag
? SmokeCommandLine.AcknowledgementFlag
: value).ToArray());
var parsed = SmokeCommandLine.Parse(complete);
var explicitObservation = SmokeCommandLine.Parse(
complete.Concat(["--observe-ms", "5000"]).ToArray());
Assert.False(missingAcknowledgement.IsValid);
Assert.Equal("missing-acknowledgement", missingAcknowledgement.ErrorCode);
Assert.False(wrongAcknowledgement.IsValid);
Assert.Equal("unknown-option", wrongAcknowledgement.ErrorCode);
Assert.True(parsed.IsValid);
Assert.True(explicitObservation.IsValid);
Assert.Equal(SmokeCommandKind.PgmCutsSequence, parsed.Invocation.Kind);
Assert.Equal(5_000, parsed.Invocation.ObservationMilliseconds);
Assert.Equal("C:\\Cuts", parsed.Invocation.SceneRoot);
Assert.Null(parsed.Invocation.ConfigPath);
Assert.Null(parsed.Invocation.PrepareSceneCode);
Assert.Null(parsed.Invocation.NextSceneCode);
}
[Theory]
[InlineData("--config")]
[InlineData("--prepare")]
[InlineData("--next")]
public void Parse_PgmCutsSequenceRejectsConfigAndCallerSelectedScenes(string option)
{
var parsed = SmokeCommandLine.Parse(
ValidPgmCutsArguments().Concat([option, "sensitive-value"]).ToArray());
Assert.False(parsed.IsValid);
Assert.Equal("unknown-option", parsed.ErrorCode);
}
[Theory]
[InlineData("4999")]
[InlineData("5001")]
[InlineData("secret")]
public void Parse_PgmCutsSequenceOnlyAcceptsExplicitFiveSecondObservation(string value)
{
var parsed = SmokeCommandLine.Parse(
ValidPgmCutsArguments().Concat(["--observe-ms", value]).ToArray());
Assert.False(parsed.IsValid);
Assert.Equal("invalid-observation-duration", parsed.ErrorCode);
}
[Theory]
[InlineData("localhost", "C:\\Cuts")]
[InlineData("192.0.2.1", "C:\\Cuts")]
[InlineData("127.0.0.1", "relative-cuts")]
public void Parse_PgmCutsSequenceRequiresLiteralLoopbackAndAbsoluteSceneRoot(
string host,
string sceneRoot)
{
var arguments = ValidPgmCutsArguments();
arguments[Array.IndexOf(arguments, "127.0.0.1")] = host;
arguments[Array.IndexOf(arguments, "C:\\Cuts")] = sceneRoot;
var parsed = SmokeCommandLine.Parse(arguments);
Assert.False(parsed.IsValid);
Assert.Equal("missing-or-invalid-required-option", parsed.ErrorCode);
}
private static SmokeCommandParseResult ValidSequenceArguments(
string prepare = "5001",
string observation = "1000") =>
SmokeCommandLine.Parse(
[
"--test-sequence",
SmokeCommandLine.AcknowledgementFlag,
"--config",
"C:\\safe\\playout.local.json",
"--prepare",
prepare,
"--next",
"5006",
"--observe-ms",
observation
]);
private static string[] ValidPgmCutsArguments() =>
[
"--pgm-cuts-sequence",
SmokeCommandLine.PgmCutsSequenceAcknowledgementFlag,
"--host",
"127.0.0.1",
"--port",
"30001",
"--expected-pgm-window-title",
"Tornado2 PGM",
"--scene-root",
"C:\\Cuts"
];
}