using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Pickers; namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Pickers; public sealed class HistorianWonderwareAddressBuilderTests { [Theory] [InlineData("SysTimeHour", "Cyclic", 60, "SysTimeHour?mode=Cyclic&interval=60")] [InlineData("ReactorTemp", "Last", 1, "ReactorTemp?mode=Last&interval=1")] [InlineData("FlowRate", "Delta", 30, "FlowRate?mode=Delta&interval=30")] public void Build_Canonical(string tag, string mode, int interval, string expected) => HistorianWonderwareAddressBuilder.Build(tag, mode, interval).ShouldBe(expected); /// A tag name carrying query-reserved characters is percent-encoded so the produced /// address stays a well-formed query string (AdminUI-005). With "A&B?C" the '&' and '?' /// must not be read as a query separator / start, so they are escaped. [Fact] public void Build_escapes_reserved_characters_in_tag_name() { var result = HistorianWonderwareAddressBuilder.Build("A&B?C", "Cyclic", 60); // The only literal '?' is the query separator the builder inserts; the only literal '&' // is the one between mode and interval. The reserved characters in the name are escaped. result.ShouldBe("A%26B%3FC?mode=Cyclic&interval=60"); result.IndexOf('?').ShouldBe(result.IndexOf("?mode=", System.StringComparison.Ordinal)); } }