using CliFx.Exceptions; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Client.CLI.Commands; using ZB.MOM.WW.OtOpcUa.Client.CLI.Tests.Fakes; namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; /// /// Regression tests for Client.CLI-011: alarm-op commands (ack, confirm, enable, disable, shelve) /// must validate the --node option with a clean CommandException rather than letting a raw /// exception propagate from NodeId.Parse inside the service. /// public class AlarmOpCommandNodeIdValidationTests { private static string HexOf(byte[] bytes) => Convert.ToHexString(bytes); /// AcknowledgeCommand with an invalid node ID must throw CommandException, not a raw exception. [Fact] public async Task AcknowledgeCommand_InvalidNodeId_ThrowsCommandException() { var fakeService = new FakeOpcUaClientService(); var factory = new FakeOpcUaClientServiceFactory(fakeService); var command = new AcknowledgeCommand(factory) { Url = "opc.tcp://localhost:4840", NodeId = "not-a-node-id", EventId = HexOf(new byte[] { 0x01 }), Comment = "" }; using var console = TestConsoleHelper.CreateConsole(); var ex = await Should.ThrowAsync(async () => await command.ExecuteAsync(console)); ex.Message.ShouldContain("node", Case.Insensitive); } /// ConfirmCommand with an invalid node ID must throw CommandException, not a raw exception. [Fact] public async Task ConfirmCommand_InvalidNodeId_ThrowsCommandException() { var fakeService = new FakeOpcUaClientService(); var factory = new FakeOpcUaClientServiceFactory(fakeService); var command = new ConfirmCommand(factory) { Url = "opc.tcp://localhost:4840", NodeId = "not-a-node-id", EventId = HexOf(new byte[] { 0x01 }), Comment = "" }; using var console = TestConsoleHelper.CreateConsole(); var ex = await Should.ThrowAsync(async () => await command.ExecuteAsync(console)); ex.Message.ShouldContain("node", Case.Insensitive); } /// EnableCommand with an invalid node ID must throw CommandException, not a raw exception. [Fact] public async Task EnableCommand_InvalidNodeId_ThrowsCommandException() { var fakeService = new FakeOpcUaClientService(); var factory = new FakeOpcUaClientServiceFactory(fakeService); var command = new EnableCommand(factory) { Url = "opc.tcp://localhost:4840", NodeId = "not-a-node-id" }; using var console = TestConsoleHelper.CreateConsole(); var ex = await Should.ThrowAsync(async () => await command.ExecuteAsync(console)); ex.Message.ShouldContain("node", Case.Insensitive); } /// DisableCommand with an invalid node ID must throw CommandException, not a raw exception. [Fact] public async Task DisableCommand_InvalidNodeId_ThrowsCommandException() { var fakeService = new FakeOpcUaClientService(); var factory = new FakeOpcUaClientServiceFactory(fakeService); var command = new DisableCommand(factory) { Url = "opc.tcp://localhost:4840", NodeId = "not-a-node-id" }; using var console = TestConsoleHelper.CreateConsole(); var ex = await Should.ThrowAsync(async () => await command.ExecuteAsync(console)); ex.Message.ShouldContain("node", Case.Insensitive); } /// ShelveCommand with an invalid node ID must throw CommandException, not a raw exception. [Fact] public async Task ShelveCommand_InvalidNodeId_ThrowsCommandException() { var fakeService = new FakeOpcUaClientService(); var factory = new FakeOpcUaClientServiceFactory(fakeService); var command = new ShelveCommand(factory) { Url = "opc.tcp://localhost:4840", NodeId = "not-a-node-id", Kind = "OneShot", DurationSeconds = 0 }; using var console = TestConsoleHelper.CreateConsole(); var ex = await Should.ThrowAsync(async () => await command.ExecuteAsync(console)); ex.Message.ShouldContain("node", Case.Insensitive); } }