using CliFx.Exceptions; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Client.CLI.Commands; using ZB.MOM.WW.OtOpcUa.Client.CLI.Tests.Fakes; using ZB.MOM.WW.OtOpcUa.Client.Shared.Models; namespace ZB.MOM.WW.OtOpcUa.Client.CLI.Tests; /// /// historyread --aggregate accepts every aggregate the client offers (which is exactly the served set), so the /// gate recipes in docs/Historian.md that need Range / Delta / PercentGood / ... can run from this CLI. /// public class HistoryReadAggregateParsingTests { /// Every client aggregate, by its name. /// Each value. public static TheoryData AllAggregates() => new(Enum.GetValues()); /// Every aggregate name (in any case) reaches the service as that aggregate. /// The aggregate. [Theory] [MemberData(nameof(AllAggregates))] public async Task Every_aggregate_name_parses(AggregateType aggregate) { var fakeService = new FakeOpcUaClientService(); var command = new HistoryReadCommand(new FakeOpcUaClientServiceFactory(fakeService)) { Url = "opc.tcp://localhost:4840", NodeId = "ns=2;s=HistNode", Aggregate = aggregate.ToString().ToUpperInvariant() }; using var console = TestConsoleHelper.CreateConsole(); await command.ExecuteAsync(console); fakeService.HistoryReadAggregateCalls.ShouldHaveSingleItem().Aggregate.ShouldBe(aggregate); } /// The short aliases still parse. /// The alias. /// The aggregate it names. [Theory] [InlineData("avg", AggregateType.Average)] [InlineData("min", AggregateType.Minimum)] [InlineData("last", AggregateType.End)] [InlineData("interp", AggregateType.Interpolative)] [InlineData("mintime", AggregateType.MinimumActualTime)] [InlineData("maxtime", AggregateType.MaximumActualTime)] public async Task Aliases_parse(string alias, AggregateType expected) { var fakeService = new FakeOpcUaClientService(); var command = new HistoryReadCommand(new FakeOpcUaClientServiceFactory(fakeService)) { Url = "opc.tcp://localhost:4840", NodeId = "ns=2;s=HistNode", Aggregate = alias }; using var console = TestConsoleHelper.CreateConsole(); await command.ExecuteAsync(console); fakeService.HistoryReadAggregateCalls.ShouldHaveSingleItem().Aggregate.ShouldBe(expected); } /// /// StandardDeviation is no longer offered (no backend serves it): it is refused before connecting, naming the /// supported set. /// [Fact] public async Task StandardDeviation_is_refused_before_connecting() { var fakeService = new FakeOpcUaClientService(); var command = new HistoryReadCommand(new FakeOpcUaClientServiceFactory(fakeService)) { Url = "opc.tcp://localhost:4840", NodeId = "ns=2;s=HistNode", Aggregate = "stddev" }; using var console = TestConsoleHelper.CreateConsole(); var ex = await Should.ThrowAsync(async () => await command.ExecuteAsync(console)); ex.Message.ShouldContain("PercentGood"); fakeService.HistoryReadAggregateCalls.ShouldBeEmpty(); } }