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-006: predictable operator-input errors must surface as /// CliFx CommandException with a clean message, not raw FormatException/ArgumentException /// with a stack trace. /// public class InputValidationErrorsTests { [Fact] public async Task HistoryReadCommand_InvalidStartTime_ThrowsCommandException() { var fakeService = new FakeOpcUaClientService(); var factory = new FakeOpcUaClientServiceFactory(fakeService); var command = new HistoryReadCommand(factory) { Url = "opc.tcp://localhost:4840", NodeId = "ns=2;s=N", StartTime = "not-a-date" }; using var console = TestConsoleHelper.CreateConsole(); var ex = await Should.ThrowAsync(async () => await command.ExecuteAsync(console)); ex.Message.ShouldContain("--start"); } [Fact] public async Task HistoryReadCommand_InvalidEndTime_ThrowsCommandException() { var fakeService = new FakeOpcUaClientService(); var factory = new FakeOpcUaClientServiceFactory(fakeService); var command = new HistoryReadCommand(factory) { Url = "opc.tcp://localhost:4840", NodeId = "ns=2;s=N", EndTime = "garbage" }; using var console = TestConsoleHelper.CreateConsole(); var ex = await Should.ThrowAsync(async () => await command.ExecuteAsync(console)); ex.Message.ShouldContain("--end"); } [Fact] public async Task HistoryReadCommand_InvalidAggregate_ThrowsCommandException() { var fakeService = new FakeOpcUaClientService(); var factory = new FakeOpcUaClientServiceFactory(fakeService); var command = new HistoryReadCommand(factory) { Url = "opc.tcp://localhost:4840", NodeId = "ns=2;s=N", Aggregate = "NotARealAggregate" }; using var console = TestConsoleHelper.CreateConsole(); var ex = await Should.ThrowAsync(async () => await command.ExecuteAsync(console)); ex.Message.ShouldContain("aggregate", Case.Insensitive); } [Fact] public async Task ReadCommand_InvalidNodeId_ThrowsCommandException() { var fakeService = new FakeOpcUaClientService(); var factory = new FakeOpcUaClientServiceFactory(fakeService); var command = new ReadCommand(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); } [Fact] public async Task SubscribeCommand_InvalidNodeId_ThrowsCommandException() { var fakeService = new FakeOpcUaClientService(); var factory = new FakeOpcUaClientServiceFactory(fakeService); var command = new SubscribeCommand(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); } }