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); } 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 ]); }