Files
wwtools/graccesscli/tests/ZB.MOM.WW.GRAccess.Cli.Tests/Commands/GRAccessSurfaceCommandTests.cs
T
Joseph Doherty 32f26272ae Initial commit: Wonderware / System Platform tools and reference
Five tools under one repo, all docs organized per DOCS-GUIDE.md:

- aalogcli: .NET 4.8 / x86 CliFx CLI for reading System Platform binary
  logs (*.aaLGX) for LLM debugging, built on aaOpenSource/aaLog. Commands:
  last, tail, range, unread, fields. Stable JSON envelope under --llm-json.
  Build template under lib/build/ for rebuilding aaLogReader.dll.

- aot: ArchestrA Object Toolkit 2014 v4.0 reference material. Dev guide
  (Markdown converted from CHM), API reference for the ArchestrA.Toolkit
  namespace, and the Monitor / Watchdog VS sample solutions.

- graccesscli: .NET 4.8 / x86 CliFx CLI that automates Galaxy
  configuration via the ArchestrA GRAccess COM interop. Includes session
  daemon, IPC protocol, and llm-json envelope contract.

- grdb: SQL/DDL exploration of the Galaxy Repository database. DDL
  captures, reusable queries, hierarchy / contained-name <-> tag-name
  translation notes.

- histdb: LLM-oriented reference for AVEVA Historian retrieval. INSQL
  linked-server, extension tables, every wwXxx time-domain extension,
  every retrieval mode, alarm/event SQL recipes, REST API. Distilled
  from the 243-page Historian Retrieval Guide.

Root contains:
- CLAUDE.md: thin index pointing into each tool's README.
- DOCS-GUIDE.md: doctrine for organizing docs for LLM consumption.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:22:20 -04:00

187 lines
7.6 KiB
C#

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(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<string, object>
{
["name"] = new[] { "A", "B" },
["confirm"] = true
});
request.Args["name"].ShouldBeAssignableTo<string[]>();
((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<string>)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");
}
private static string DispatcherSource()
{
var directory = new DirectoryInfo(Directory.GetCurrentDirectory());
while (directory != null)
{
var sourcePath = Path.Combine(
directory.FullName,
"src",
"ZB.MOM.WW.GRAccess.Cli",
"GRAccess",
"GRAccessCommandDispatcher.cs");
if (File.Exists(sourcePath))
return File.ReadAllText(sourcePath);
directory = directory.Parent;
}
throw new FileNotFoundException("Could not locate GRAccessCommandDispatcher.cs from the test working directory.");
}
}
}