using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Drivers.DeviceForms; namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns; /// /// Round-trip guard for the Modbus device endpoint model — the gate-critical DeviceConfig /// surface. Endpoint keys MUST be PascalCase (Host/Port/UnitId) so the merged probe config binds via /// the runtime's case-insensitive deserializer, matching the seed and Wave-B service tests. /// public sealed class ModbusDeviceModelTests { [Theory] [InlineData(null)] [InlineData("")] [InlineData(" ")] [InlineData("{}")] public void FromJson_returns_defaults_for_empty_input(string? json) { var m = ModbusDeviceModel.FromJson(json); m.Host.ShouldBe("127.0.0.1"); m.Port.ShouldBe(502); m.UnitId.ShouldBe(1); } [Fact] public void Round_trip_preserves_endpoint_fields() { var m = new ModbusDeviceModel { Host = "10.100.0.35", Port = 5020, UnitId = 7 }; var json = m.ToJson(); var m2 = ModbusDeviceModel.FromJson(json); m2.Host.ShouldBe("10.100.0.35"); m2.Port.ShouldBe(5020); m2.UnitId.ShouldBe(7); } [Fact] public void ToJson_emits_PascalCase_endpoint_keys() { var json = new ModbusDeviceModel { Host = "10.1.2.3", Port = 5020, UnitId = 1 }.ToJson(); // Runtime binds ModbusDriverOptions.Host/Port/UnitId from these exact PascalCase keys. json.ShouldContain("\"Host\":\"10.1.2.3\""); json.ShouldContain("\"Port\":5020"); json.ShouldContain("\"UnitId\":1"); } [Fact] public void FromJson_then_ToJson_preserves_unknown_keys() { var json = ModbusDeviceModel .FromJson("""{"Host":"10.1.2.3","Port":5020,"UnitId":1,"customKey":"keepme"}""") .ToJson(); json.ShouldContain("customKey"); json.ShouldContain("keepme"); json.ShouldContain("\"Host\":\"10.1.2.3\""); } [Fact] public void Validate_flags_blank_host() { new ModbusDeviceModel { Host = "" }.Validate().ShouldNotBeNull(); new ModbusDeviceModel { Host = "127.0.0.1" }.Validate().ShouldBeNull(); } }