using Opc.Ua; using ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters; namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests.Adapters; /// /// ScadaBridge#125: SDK 2's legacy boxing hands back SDK wrapper types where SDK 1.5 handed back /// CLR ones. The DCL boundary must restore the SDK 1.5 payload shapes the codec, scripts and the /// alarm mapper were written against, and must not alias SDK buffers. /// public class OpcUaValueBoundaryTests { [Fact] public void Scalars_KeepTheirClrTypes() { Assert.Equal(42, OpcUaValueBoundary.ToClr(Variant.From(42))); Assert.Equal(42.5, OpcUaValueBoundary.ToClr(Variant.From(42.5))); Assert.Equal(true, OpcUaValueBoundary.ToClr(Variant.From(true))); Assert.Equal("abc", OpcUaValueBoundary.ToClr(Variant.From("abc"))); Assert.Equal((ushort)500, OpcUaValueBoundary.ToClr(Variant.From((ushort)500))); Assert.Null(OpcUaValueBoundary.ToClr(Variant.Null)); } [Fact] public void ByteString_BecomesAByteArray() { var value = OpcUaValueBoundary.ToClr(Variant.From(ByteString.From(new byte[] { 1, 2, 3 }))); Assert.Equal(new byte[] { 1, 2, 3 }, Assert.IsType(value)); } [Fact] public void DateTime_BecomesAUtcDateTime_AndNoTimestampIsMinValue() { var when = new DateTime(2026, 11, 12, 10, 30, 0, DateTimeKind.Utc); var value = Assert.IsType(OpcUaValueBoundary.ToClr(Variant.From(when))); Assert.Equal(when, value); Assert.Equal(DateTimeKind.Utc, value.Kind); Assert.Equal(DateTime.MinValue, OpcUaValueBoundary.ToClrTimestamp(DateTimeUtc.MinValue)); Assert.Equal(when, OpcUaValueBoundary.ToClrTimestamp(when)); } [Fact] public void Guid_BecomesASystemGuid() { var id = Guid.NewGuid(); Assert.Equal(id, Assert.IsType(OpcUaValueBoundary.ToClr(Variant.From(new Uuid(id))))); } [Fact] public void Int32Array_BecomesADetachedClrArray() { var source = new[] { 1, 2, 3 }; var value = Assert.IsType(OpcUaValueBoundary.ToClr(Variant.From((ArrayOf)source))); Assert.Equal(source, value); Assert.NotSame(source, value); } [Fact] public void ToVariant_ByteArray_IsAByteStringScalar() { // SDK 1.5's new Variant(byte[]) was a ByteString scalar; a Byte[] array would change the wire type. var variant = OpcUaValueBoundary.ToVariant(new byte[] { 9, 8 }); Assert.Equal(BuiltInType.ByteString, variant.TypeInfo.BuiltInType); Assert.Equal(ValueRanks.Scalar, variant.TypeInfo.ValueRank); } [Fact] public void ToVariant_Int32Array_IsAOneDimensionalInt32Array_AndCopiesTheBuffer() { var source = new[] { 4, 5, 6 }; var variant = OpcUaValueBoundary.ToVariant(source); source[0] = 99; Assert.Equal(BuiltInType.Int32, variant.TypeInfo.BuiltInType); Assert.Equal(ValueRanks.OneDimension, variant.TypeInfo.ValueRank); Assert.Equal(new[] { 4, 5, 6 }, Assert.IsType(OpcUaValueBoundary.ToClr(variant))); } [Fact] public void ToVariant_Scalars_MapToTheirBuiltInType() { Assert.Equal(BuiltInType.Double, OpcUaValueBoundary.ToVariant(1.5).TypeInfo.BuiltInType); Assert.Equal(BuiltInType.Boolean, OpcUaValueBoundary.ToVariant(true).TypeInfo.BuiltInType); Assert.Equal(BuiltInType.String, OpcUaValueBoundary.ToVariant("x").TypeInfo.BuiltInType); Assert.True(OpcUaValueBoundary.ToVariant(null).IsNull); } [Fact] public void ToVariant_UnsupportedType_Throws() { Assert.Throws(() => OpcUaValueBoundary.ToVariant(new object())); Assert.Throws(() => OpcUaValueBoundary.ToVariant(new int[2, 2])); } }