using System.Text.Json; using System.Text.Json.Serialization; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Driver.S7; namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests; public sealed class S7DriverPageFormSerializationTests { private static readonly JsonSerializerOptions _opts = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = false, }; [Fact] public void RoundTrip_PreservesKnownFields() { var original = new S7DriverOptions { Host = "10.0.0.5", Port = 102, CpuType = S7CpuType.S71200, Rack = 0, Slot = 1, Timeout = TimeSpan.FromSeconds(10), Probe = new S7ProbeOptions { Enabled = false, Interval = TimeSpan.FromSeconds(15), Timeout = TimeSpan.FromSeconds(3), }, ProbeTimeoutSeconds = 30, Tags = [], }; var json = JsonSerializer.Serialize(original, _opts); var back = JsonSerializer.Deserialize(json, _opts); back.ShouldNotBeNull(); back.Host.ShouldBe("10.0.0.5"); back.Port.ShouldBe(102); back.CpuType.ShouldBe(S7CpuType.S71200); back.Rack.ShouldBe((short)0); back.Slot.ShouldBe((short)1); back.Timeout.ShouldBe(TimeSpan.FromSeconds(10)); back.Probe.ShouldNotBeNull(); back.Probe.Enabled.ShouldBeFalse(); back.Probe.Interval.ShouldBe(TimeSpan.FromSeconds(15)); back.Probe.Timeout.ShouldBe(TimeSpan.FromSeconds(3)); back.ProbeTimeoutSeconds.ShouldBe(30); back.Tags.ShouldBeEmpty(); } [Fact] public void Deserialize_DropsUnknownFields() { var jsonWithExtra = """{"unknownField":"old-value","probeTimeoutSeconds":12}"""; var optsSkip = new JsonSerializerOptions(_opts) { UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip, }; var back = JsonSerializer.Deserialize(jsonWithExtra, optsSkip); back.ShouldNotBeNull(); back.ProbeTimeoutSeconds.ShouldBe(12); } [Fact] public void FormModel_RoundTrip_PreservesEditableFields() { var tags = new[] { new S7TagDefinition("Speed", "DB1.DBD0", S7DataType.Float32, Writable: true), new S7TagDefinition("Status", "DB1.DBW4", S7DataType.Int16, Writable: false), }; var opts = new S7DriverOptions { Host = "192.168.1.50", Port = 102, CpuType = S7CpuType.S7300, Rack = 0, Slot = 2, Timeout = TimeSpan.FromSeconds(7), Probe = new S7ProbeOptions { Enabled = true, Interval = TimeSpan.FromSeconds(8), Timeout = TimeSpan.FromSeconds(4), }, ProbeTimeoutSeconds = 20, Tags = tags, }; var form = ZB.MOM.WW.OtOpcUa.AdminUI.Components.Pages.Clusters.Drivers .S7DriverPage.FormModel.FromOptions(opts); var roundTripped = form.ToOptions(); roundTripped.Host.ShouldBe("192.168.1.50"); roundTripped.Port.ShouldBe(102); roundTripped.CpuType.ShouldBe(S7CpuType.S7300); roundTripped.Rack.ShouldBe((short)0); roundTripped.Slot.ShouldBe((short)2); roundTripped.Timeout.ShouldBe(TimeSpan.FromSeconds(7)); roundTripped.Probe.Enabled.ShouldBeTrue(); roundTripped.Probe.Interval.ShouldBe(TimeSpan.FromSeconds(8)); roundTripped.Probe.Timeout.ShouldBe(TimeSpan.FromSeconds(4)); roundTripped.ProbeTimeoutSeconds.ShouldBe(20); // Tags must survive the FormModel round-trip unchanged (regression guard for the // Tags = [] data-loss bug fixed in this PR). roundTripped.Tags.Count.ShouldBe(2); roundTripped.Tags[0].Name.ShouldBe("Speed"); roundTripped.Tags[0].Address.ShouldBe("DB1.DBD0"); roundTripped.Tags[0].DataType.ShouldBe(S7DataType.Float32); roundTripped.Tags[1].Name.ShouldBe("Status"); roundTripped.Tags[1].Writable.ShouldBeFalse(); } }