using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Configuration.Entities; namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests; /// /// Covers follow-up E projection: carries the equipment's /// DriverInstanceId / DeviceId bindings and the resolved DeviceHost (parsed from /// the bound 's schemaless DeviceConfig JSON via the shared /// DeviceConfigIntent.TryExtractHost). A later task grafts a driver's discovered /// FixedTree onto a zero-tag equipment and partitions a multi-device driver by host using these. /// The direct host-extraction + normalization unit tests moved to /// Commons.Tests/DeviceConfigIntentTests (R2-11) — this suite keeps the Compose-level projection /// coverage. /// public sealed class AddressSpaceComposerDeviceHostTests { /// An equipment bound to a driver + a device whose config carries a top-level /// HostAddress resolves all three fields, with the host trimmed + lower-cased. [Fact] public void Compose_resolves_driver_device_and_device_host() { var equipment = new[] { NewEquipment("eq-1", driver: "d1", device: "dev1") }; var devices = new[] { NewDevice("dev1", "d1", "{\"HostAddress\":\"10.0.0.5:8193\"}") }; var node = AddressSpaceComposer.Compose( Array.Empty(), Array.Empty(), equipment, Array.Empty(), Array.Empty(), devices: devices) .EquipmentNodes.ShouldHaveSingleItem(); node.EquipmentId.ShouldBe("eq-1"); node.DriverInstanceId.ShouldBe("d1"); node.DeviceId.ShouldBe("dev1"); node.DeviceHost.ShouldBe("10.0.0.5:8193"); } /// An equipment with no driver and no device → all three new fields null (driver-less, /// no device to resolve a host from). [Fact] public void Compose_equipment_without_driver_or_device_yields_null_bindings() { var equipment = new[] { NewEquipment("eq-1", driver: null, device: null) }; var node = AddressSpaceComposer.Compose( Array.Empty(), Array.Empty(), equipment, Array.Empty(), Array.Empty(), devices: Array.Empty()) .EquipmentNodes.ShouldHaveSingleItem(); node.DriverInstanceId.ShouldBeNull(); node.DeviceId.ShouldBeNull(); node.DeviceHost.ShouldBeNull(); } /// A bound DeviceId with no matching device row, or a device whose config has no /// HostAddress, resolves DeviceHost to null while DeviceId is still carried. [Fact] public void Compose_device_host_is_null_when_unresolvable() { var equipment = new[] { NewEquipment("eq-missing", driver: "d1", device: "dev-missing"), NewEquipment("eq-nohost", driver: "d1", device: "dev-nohost"), }; var devices = new[] { NewDevice("dev-nohost", "d1", "{\"Port\":502}") }; var nodes = AddressSpaceComposer.Compose( Array.Empty(), Array.Empty(), equipment, Array.Empty(), Array.Empty(), devices: devices) .EquipmentNodes; var missing = nodes.Single(n => n.EquipmentId == "eq-missing"); missing.DeviceId.ShouldBe("dev-missing"); missing.DeviceHost.ShouldBeNull(); var noHost = nodes.Single(n => n.EquipmentId == "eq-nohost"); noHost.DeviceId.ShouldBe("dev-nohost"); noHost.DeviceHost.ShouldBeNull(); } private static Equipment NewEquipment(string id, string? driver, string? device) => new() { EquipmentId = id, DriverInstanceId = driver, DeviceId = device, UnsLineId = "line-1", Name = id, MachineCode = id.ToUpperInvariant(), }; private static Device NewDevice(string deviceId, string driverInstanceId, string deviceConfig) => new() { DeviceId = deviceId, DriverInstanceId = driverInstanceId, Name = deviceId, DeviceConfig = deviceConfig, }; }