using System.Text.Json; using System.Text.Json.Serialization; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.Forms; using ZB.MOM.WW.OtOpcUa.Driver.TwinCAT; namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests; public sealed class TwinCATDriverPageFormSerializationTests { private static readonly JsonSerializerOptions _opts = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = false, }; [Fact] public void RoundTrip_PreservesKnownFields() { var original = new TwinCATDriverOptions { Timeout = TimeSpan.FromSeconds(4), UseNativeNotifications = false, EnableControllerBrowse = true, NotificationMaxDelayMs = 50, Probe = new TwinCATProbeOptions { Enabled = false, Interval = TimeSpan.FromSeconds(10), Timeout = TimeSpan.FromSeconds(3), }, ProbeTimeoutSeconds = 20, Devices = [], }; var json = JsonSerializer.Serialize(original, _opts); var back = JsonSerializer.Deserialize(json, _opts); back.ShouldNotBeNull(); back.Timeout.ShouldBe(TimeSpan.FromSeconds(4)); back.UseNativeNotifications.ShouldBeFalse(); back.EnableControllerBrowse.ShouldBeTrue(); back.NotificationMaxDelayMs.ShouldBe(50); back.Probe.ShouldNotBeNull(); back.Probe.Enabled.ShouldBeFalse(); back.Probe.Interval.ShouldBe(TimeSpan.FromSeconds(10)); back.Probe.Timeout.ShouldBe(TimeSpan.FromSeconds(3)); back.ProbeTimeoutSeconds.ShouldBe(20); back.Devices.ShouldBeEmpty(); } [Fact] public void Deserialize_DropsUnknownFields() { var jsonWithExtra = """{"unknownField":"old-value","probeTimeoutSeconds":25}"""; var optsSkip = new JsonSerializerOptions(_opts) { UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, }; var back = JsonSerializer.Deserialize(jsonWithExtra, optsSkip); back.ShouldNotBeNull(); back.ProbeTimeoutSeconds.ShouldBe(25); } [Fact] public void FormModel_RoundTrip_PreservesEditableFields() { var opts = new TwinCATDriverOptions { Timeout = TimeSpan.FromSeconds(3), UseNativeNotifications = true, EnableControllerBrowse = false, NotificationMaxDelayMs = 100, Probe = new TwinCATProbeOptions { Enabled = true, Interval = TimeSpan.FromSeconds(6), Timeout = TimeSpan.FromSeconds(2), }, ProbeTimeoutSeconds = 15, }; var form = TwinCATDriverForm.FormModel.FromOptions(opts); var roundTripped = form.ToOptions([]); roundTripped.Timeout.ShouldBe(TimeSpan.FromSeconds(3)); roundTripped.UseNativeNotifications.ShouldBeTrue(); roundTripped.EnableControllerBrowse.ShouldBeFalse(); roundTripped.NotificationMaxDelayMs.ShouldBe(100); roundTripped.Probe.Enabled.ShouldBeTrue(); roundTripped.Probe.Interval.ShouldBe(TimeSpan.FromSeconds(6)); roundTripped.Probe.Timeout.ShouldBe(TimeSpan.FromSeconds(2)); roundTripped.ProbeTimeoutSeconds.ShouldBe(15); } // v3 Batch-2 (WP3): the embedded-Devices CollectionEditor + TwinCATDeviceRow retired — devices are now // separate Device entities authored via the raw-tree DeviceModal (TwinCATDeviceModel, HostAddress into // DeviceConfig). The former DeviceRow_* tests were removed. The channel-config FormModel now lives in // TwinCATDriverForm; the multi-device Devices array it preserves is covered below. [Fact] public void FormModel_ToOptions_SerializesDeviceList() { var form = TwinCATDriverForm.FormModel.FromOptions(new TwinCATDriverOptions()); var devices = new[] { new TwinCATDeviceOptions("192.168.0.1.1.1:851", "PLC1") }; var opts = form.ToOptions(devices); var json = JsonSerializer.Serialize(opts, _opts); var back = JsonSerializer.Deserialize(json, _opts); back.ShouldNotBeNull(); back.Devices.Count.ShouldBe(1); back.Devices[0].HostAddress.ShouldBe("192.168.0.1.1.1:851"); back.Devices[0].DeviceName.ShouldBe("PLC1"); } }