using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using CliFx.Attributes; using Shouldly; using Xunit; using ZB.MOM.WW.GRAccess.Cli.Commands; using ZB.MOM.WW.GRAccess.Cli.Protocol; namespace ZB.MOM.WW.GRAccess.Cli.Tests.Commands { public class GRAccessSurfaceCommandTests { [Theory] [InlineData(typeof(GalaxyInfoCommand), "galaxy info")] [InlineData(typeof(GalaxyImportScriptLibraryCommand), "galaxy import-script-library")] [InlineData(typeof(ObjectGetCommand), "object get")] [InlineData(typeof(ObjectQueryNameCommand), "object query-name")] [InlineData(typeof(ObjectScriptsCreateCommand), "object scripts create")] [InlineData(typeof(ObjectScriptsSettingsSetCommand), "object scripts settings set")] [InlineData(typeof(ObjectScriptsSetCommand), "object scripts set")] [InlineData(typeof(ObjectScriptsDeleteCommand), "object scripts delete")] [InlineData(typeof(TemplateDeriveCommand), "template derive")] [InlineData(typeof(InstanceDeployCommand), "instance deploy")] [InlineData(typeof(ObjectsExportCommand), "objects export")] [InlineData(typeof(ToolsetListCommand), "toolset list")] [InlineData(typeof(ScriptLibraryExportCommand), "script-library export")] [InlineData(typeof(SecurityRolesCommand), "security roles")] [InlineData(typeof(SettingsLocaleGetCommand), "settings locale get")] public void Command_IsRegistered(Type commandType, string expectedName) { var attr = (CommandAttribute)Attribute.GetCustomAttribute( commandType, typeof(CommandAttribute)); attr.ShouldNotBeNull(); attr.Name.ShouldBe(expectedName); } [Fact] public void PipeRequest_StoresStructuredArgs() { var request = PipeRequest.Execute( "objects", "checkout", new Dictionary { ["name"] = new[] { "A", "B" }, ["confirm"] = true }); request.Args["name"].ShouldBeAssignableTo(); ((string[])request.Args["name"]).ShouldBe(new[] { "A", "B" }); request.Args["confirm"].ShouldBe(true); } [Fact] public void ConfirmedCommand_IncludesConfirmationArgs() { var command = new ObjectCheckoutCommand { GalaxyName = "ZB", ObjectName = "DEV", Confirm = true, ConfirmTarget = "DEV" }; var args = command.Args(); args["confirm"].ShouldBe(true); args["confirm-target"].ShouldBe("DEV"); args["name"].ShouldBe("DEV"); } [Fact] public void BulkCommand_PreservesMultipleNames() { var command = new ObjectsCheckoutCommand { GalaxyName = "ZB", Names = new[] { "A", "B" }, Confirm = true, ConfirmTarget = "A,B" }; var names = ((IReadOnlyList)command.Args()["name"]).ToArray(); names.ShouldBe(new[] { "A", "B" }); } [Fact] public void UdaCommands_DefaultToValidWritableCategory() { new ObjectUdaAddCommand().Args()["category"].ShouldBe("MxCategoryWriteable_USC"); new ObjectUdaUpdateCommand().Args()["category"].ShouldBe("MxCategoryWriteable_USC"); } [Fact] public void TemplateDelete_DefaultsToNonCascadingForceOption() { new TemplateDeleteCommand().Args()["force-option"].ShouldBe("dontForceTemplateDelete"); } [Fact] public void InstanceDelete_DefaultsToDeleteCleanupForceOption() { new InstanceDeleteCommand().Args()["force-option"].ShouldBe("undeployIfDeployed"); } [Theory] [InlineData("EForceDeleteTemplateOption", "dontForceTemplateDelete", "dontForceTemplateDelete")] [InlineData("EForceDeleteTemplateOption", "galaxy_dontForceTemplateDelete", "dontForceTemplateDelete")] [InlineData("MxAttributeCategory", "mxcategorywriteable_usc", "MxCategoryWriteable_USC")] public void DispatcherEnumParser_AcceptsCaseInsensitiveNamesAndGalaxyPrefixAliases( string enumType, string value, string expected) { var dispatcher = typeof(GalaxyInfoCommand).Assembly.GetType("ZB.MOM.WW.GRAccess.Cli.GRAccess.GRAccessCommandDispatcher"); var enumValue = dispatcher.GetMethod("EnumValue", BindingFlags.Static | BindingFlags.NonPublic, null, new[] { typeof(string), typeof(string) }, null) .Invoke(null, new object[] { enumType, value }); enumValue.ToString().ShouldBe(expected); } [Theory] [InlineData("AnalogLimitAlarm", "AnalogExtension")] [InlineData("analoglimitalarm", "AnalogExtension")] [InlineData("HistoryExtension", "HistoryExtension")] public void DispatcherExtensionType_NormalizesLegacyAliases(string value, string expected) { var dispatcher = typeof(GalaxyInfoCommand).Assembly.GetType("ZB.MOM.WW.GRAccess.Cli.GRAccess.GRAccessCommandDispatcher"); var extensionType = dispatcher.GetMethod("ExtensionType", BindingFlags.Static | BindingFlags.NonPublic) .Invoke(null, new object[] { value }); extensionType.ShouldBe(expected); } [Fact] public void DispatcherAtomicObjectEdit_UsesExpectedLifecycle() { var source = DispatcherSource(); var helper = source.Substring(source.IndexOf("private static string AtomicObjectEdit", StringComparison.Ordinal)); helper.IndexOf("obj.CheckOut();", StringComparison.Ordinal).ShouldBeLessThan( helper.IndexOf("var summary = mutation(obj);", StringComparison.Ordinal)); helper.IndexOf("var summary = mutation(obj);", StringComparison.Ordinal).ShouldBeLessThan( helper.IndexOf("obj.Save();", StringComparison.Ordinal)); helper.IndexOf("obj.Save();", StringComparison.Ordinal).ShouldBeLessThan( helper.IndexOf("obj.CheckIn(string.Empty);", StringComparison.Ordinal)); helper.ShouldContain("obj.UndoCheckOut();"); } [Fact] public void DispatcherSingleObjectMutations_UseAtomicEditHelper() { var source = DispatcherSource(); source.ShouldContain("return AtomicObjectEdit(obj, editObj =>"); source.ShouldContain("return AtomicObjectEdit(FindSingleObject(galaxy, Kind(args), Arg(args, \"name\")), obj => ObjectScriptCreate"); source.ShouldContain("return AtomicObjectEdit(FindSingleObject(galaxy, Kind(args), Arg(args, \"name\")), obj => ObjectScriptSet"); source.ShouldContain("return AtomicObjectEdit(FindSingleObject(galaxy, Kind(args), Arg(args, \"name\")), obj => ObjectScriptSettingsSet"); } [Fact] public void DispatcherScriptDeleteMutation_RequiresConfirmation() { var source = DispatcherSource(); var branch = source.Substring( source.IndexOf("case \"scripts-delete\":", StringComparison.Ordinal), source.IndexOf("case \"scripts-set\":", StringComparison.Ordinal) - source.IndexOf("case \"scripts-delete\":", StringComparison.Ordinal)); branch.ShouldContain("RequireConfirm(args, Arg(args, \"name\"));"); } // Removed: tests asserting EnsureMutableViaSetValue and "package-only" // wording in command descriptions. Both reflected an earlier (incorrect) // belief that IAttribute.SetValue silently no-ops on // MxCategoryPackageOnly_Lockable script-text fields. Live round-trip // (analysis/ide-edit-investigation/probe_setvalue/) and AVEVA Tech Note // 537 confirm writes via ConfigurableAttributes[