using System.Text.Json; using System.Text.Json.Serialization; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Clusters.Drivers; using ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware.Client; namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests; public sealed class HistorianWonderwareDriverPageFormSerializationTests { private static readonly JsonSerializerOptions _opts = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = false, }; [Fact] public void RoundTrip_PreservesKnownFields() { var original = new WonderwareHistorianClientOptions( PipeName: "otopcua-historian-prod", SharedSecret: "t0ps3cr3t", PeerName: "OtOpcUa-Primary", ConnectTimeout: TimeSpan.FromSeconds(20), CallTimeout: TimeSpan.FromSeconds(60)) { ProbeTimeoutSeconds = 25, }; var json = JsonSerializer.Serialize(original, _opts); var back = JsonSerializer.Deserialize(json, _opts); back.ShouldNotBeNull(); back.PipeName.ShouldBe("otopcua-historian-prod"); back.SharedSecret.ShouldBe("t0ps3cr3t"); back.PeerName.ShouldBe("OtOpcUa-Primary"); back.ConnectTimeout.ShouldBe(TimeSpan.FromSeconds(20)); back.CallTimeout.ShouldBe(TimeSpan.FromSeconds(60)); back.EffectiveConnectTimeout.ShouldBe(TimeSpan.FromSeconds(20)); back.EffectiveCallTimeout.ShouldBe(TimeSpan.FromSeconds(60)); back.ProbeTimeoutSeconds.ShouldBe(25); } [Fact] public void RoundTrip_NullTimeouts_UsesDefaults() { var original = new WonderwareHistorianClientOptions( PipeName: "otopcua-historian", SharedSecret: "secret"); var json = JsonSerializer.Serialize(original, _opts); var back = JsonSerializer.Deserialize(json, _opts); back.ShouldNotBeNull(); back.ConnectTimeout.ShouldBeNull(); back.CallTimeout.ShouldBeNull(); back.EffectiveConnectTimeout.ShouldBe(TimeSpan.FromSeconds(10)); back.EffectiveCallTimeout.ShouldBe(TimeSpan.FromSeconds(30)); } [Fact] public void Deserialize_DropsUnknownFields() { var jsonWithExtra = """ { "unknownField": "old-value", "pipeName": "otopcua-historian", "sharedSecret": "s3cr3t", "probeTimeoutSeconds": 20 } """; var optsWithSkip = new JsonSerializerOptions(_opts) { UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, }; var back = JsonSerializer.Deserialize(jsonWithExtra, optsWithSkip); back.ShouldNotBeNull(); back.ProbeTimeoutSeconds.ShouldBe(20); back.PipeName.ShouldBe("otopcua-historian"); } [Fact] public void FormModel_RoundTrip_PreservesAllFields() { // Construct a record with non-default values for every property and verify // that WonderwareHistorianClientFormModel.FromRecord → ToRecord is lossless. var original = new WonderwareHistorianClientOptions( PipeName: "otopcua-historian-prod", SharedSecret: "sup3rs3cr3t", PeerName: "OtOpcUa-Redundant", ConnectTimeout: TimeSpan.FromSeconds(18), CallTimeout: TimeSpan.FromSeconds(45)) { ProbeTimeoutSeconds = 30, }; var form = HistorianWonderwareDriverPage.WonderwareHistorianClientFormModel.FromRecord(original); var result = form.ToRecord(); result.PipeName.ShouldBe("otopcua-historian-prod"); result.SharedSecret.ShouldBe("sup3rs3cr3t"); result.PeerName.ShouldBe("OtOpcUa-Redundant"); result.ConnectTimeout.ShouldBe(TimeSpan.FromSeconds(18)); result.CallTimeout.ShouldBe(TimeSpan.FromSeconds(45)); result.EffectiveConnectTimeout.ShouldBe(TimeSpan.FromSeconds(18)); result.EffectiveCallTimeout.ShouldBe(TimeSpan.FromSeconds(45)); result.ProbeTimeoutSeconds.ShouldBe(30); } }