using System.Text; using AVEVA.Historian.Client.Wcf; namespace AVEVA.Historian.Client.Tests; public sealed class WcfOpen2ProtocolTests { [Fact] public void LegacyVersion1SerializerMatchesDecompiledSaveOpenConnectionParamsLayout() { byte[] actual = HistorianOpen2Protocol.SerializeLegacyVersion1(new HistorianOpen2Request( HostName: "H", ProcessName: "P", ProcessId: 0x01020304, UserName: "U", Password: Encoding.Unicode.GetBytes("pw"), ClientType: 4, ClientVersion: 11, ConnectionMode: 2, MetadataNamespace: HistorianMetadataNamespace.Empty)); byte[] expected = [ 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x00, 0x04, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x55, 0x00, 0x04, 0x00, 0x00, 0x00, 0x70, 0x00, 0x77, 0x00, 0x04, 0x0B, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ]; Assert.Equal(expected, actual); } [Fact] public void LegacyVersion1SerializerUsesUtf16CodeUnitStringLengths() { byte[] actual = HistorianOpen2Protocol.SerializeLegacyVersion1(new HistorianOpen2Request( HostName: "A\ud83d\ude00", ProcessName: string.Empty, ProcessId: 0, UserName: string.Empty, Password: [], ClientType: 4, ClientVersion: 0, ConnectionMode: 2, MetadataNamespace: HistorianMetadataNamespace.Empty)); Assert.Equal([0x03, 0x00, 0x00, 0x00], actual[2..6]); Assert.Equal(Encoding.Unicode.GetBytes("A\ud83d\ude00"), actual[6..12]); } [Fact] public void NativeErrorParserReadsObservedFiveByteBuffers() { HistorianNativeError? error = HistorianOpen2Protocol.TryReadNativeError([0x04, 0xAB, 0x00, 0x00, 0x00]); Assert.NotNull(error); Assert.Equal(4, error.Type); Assert.Equal(171, error.Code); Assert.Equal("AuthenticationFailed", error.Name); } [Fact] public void NativeErrorParserRejectsShortBuffers() { Assert.Null(HistorianOpen2Protocol.TryReadNativeError([0x04, 0xAB, 0x00, 0x00])); } [Fact] public void LegacyOpen2OutputParserReadsObservedWcfLayout() { byte[] buffer = [ 0x78, 0x56, 0x34, 0x12, 0x33, 0x22, 0x11, 0x00, 0x55, 0x44, 0x77, 0x66, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x44, 0x33, 0x22, 0x11 ]; HistorianLegacyOpen2Output? output = HistorianOpen2Protocol.TryReadLegacyOpen2Output(buffer); Assert.NotNull(output); Assert.Equal(0x12345678, output.Handle); Assert.Equal(new Guid("00112233-4455-6677-8899-aabbccddeeff"), output.StorageSessionId); Assert.Equal(0x0102030405060708, output.ConnectTimeFileTimeUtc); Assert.Equal(0x11223344, output.ServerStatus); } [Fact] public void LegacyOpen2OutputParserRejectsNonLegacyLength() { Assert.Null(HistorianOpen2Protocol.TryReadLegacyOpen2Output([0x00])); } [Fact] public void NativeOpen3OutputParserReadsObservedDeserializerLayout() { byte[] buffer = [ 0x03, 0x78, 0x56, 0x34, 0x12, 0x33, 0x22, 0x11, 0x00, 0x55, 0x44, 0x77, 0x66, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x44, 0x33, 0x22, 0x11, 0x00 ]; HistorianNativeOpen3Output? output = HistorianOpen2Protocol.TryReadNativeOpen3Output(buffer); Assert.NotNull(output); Assert.Equal(3, output.ProtocolVersion); Assert.Equal(0x12345678, output.Handle); Assert.Equal(new Guid("00112233-4455-6677-8899-aabbccddeeff"), output.StorageSessionId); Assert.Equal(0x0102030405060708, output.ConnectTimeFileTimeUtc); Assert.Equal(0x1112131415161718, output.ServerTimeFileTimeUtc); Assert.Equal([0x44, 0x33, 0x22, 0x11, 0x00], output.TrailingBytes); } [Fact] public void NativeOpen3OutputParserRejectsUnsupportedVersion() { Assert.Null(HistorianOpen2Protocol.TryReadNativeOpen3Output([0x01, 0x00, 0x00, 0x00])); } [Fact] public void NativeVersion3SerializerMatchesDecompiledFieldOrder() { byte[] actual = HistorianOpen2Protocol.SerializeNativeVersion3( new HistorianOpen2Request( HostName: "H", ProcessName: "P", ProcessId: 0x01020304, UserName: string.Empty, Password: [0xAA, 0xBB], ClientType: 4, ClientVersion: 11, ConnectionMode: 1026, MetadataNamespace: HistorianMetadataNamespace.Empty), new HistorianClientCommonInfo( FormatVersion: 3, ServerNodeName: "S", ClientNodeName: "C", ProcessId: 0x11223344, HcalVersion: 17, ProcessName: "Proc", Proxy: string.Empty, DataSourceId: string.Empty, ShardId: new Guid("00112233-4455-6677-8899-aabbccddeeff"), ClientVersion: 0x55667788, ClientTimestamp: 0x0102030405060708, ClientDllVersion: string.Empty)); byte[] expectedPrefix = [ 0x03, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x02, 0x00, 0xAA, 0xBB, 0x04, 0x02, 0x04, 0x00, 0x00 ]; Assert.Equal(expectedPrefix, actual[..expectedPrefix.Length]); Assert.Contains(0x03, actual); byte[] expectedSuffix = [0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]; Assert.Equal(expectedSuffix, actual[^expectedSuffix.Length..]); } [Fact] public void NativeOpenConnection3Version6SerializerAddsObservedPrefixBeforeContent() { HistorianOpen2Request request = new( HostName: "H", ProcessName: "P", ProcessId: 0x01020304, UserName: string.Empty, Password: [0xAA, 0xBB], ClientType: 4, ClientVersion: 11, ConnectionMode: 1026, MetadataNamespace: HistorianMetadataNamespace.Empty); HistorianClientCommonInfo commonInfo = new( FormatVersion: 3, ServerNodeName: "S", ClientNodeName: "C", ProcessId: 0x11223344, HcalVersion: 17, ProcessName: "Proc", Proxy: string.Empty, DataSourceId: string.Empty, ShardId: new Guid("00112233-4455-6677-8899-aabbccddeeff"), ClientVersion: 0x55667788, ClientTimestamp: 0x0102030405060708, ClientDllVersion: string.Empty); byte[] actual = HistorianOpen2Protocol.SerializeNativeOpenConnection3Version6( request, commonInfo, new Guid("00112233-4455-6677-8899-aabbccddeeff")); byte[] expectedPrefix = [ 0x06, 0x33, 0x22, 0x11, 0x00, 0x55, 0x44, 0x77, 0x66, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00 ]; Assert.Equal(expectedPrefix, actual[..expectedPrefix.Length]); byte[] expectedContentPrefix = [ 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x02, 0x00, 0xAA, 0xBB, 0x04, 0x02, 0x04, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00 ]; Assert.Equal(expectedContentPrefix, actual[expectedPrefix.Length..(expectedPrefix.Length + expectedContentPrefix.Length)]); } [Fact] public void NativeOpenConnection3Version6SerializerCanUseSeparateCredentialBlock() { HistorianOpen2Request request = new( HostName: "H", ProcessName: "P", ProcessId: 0x01020304, UserName: string.Empty, Password: [0xAA, 0xBB], ClientType: 4, ClientVersion: 11, ConnectionMode: 1026, MetadataNamespace: HistorianMetadataNamespace.Empty); HistorianClientCommonInfo commonInfo = new( FormatVersion: 2, ServerNodeName: string.Empty, ClientNodeName: string.Empty, ProcessId: 0, HcalVersion: 17, ProcessName: string.Empty, Proxy: string.Empty, DataSourceId: string.Empty, ShardId: Guid.Empty, ClientVersion: 0, ClientTimestamp: 0, ClientDllVersion: string.Empty); byte[] actual = HistorianOpen2Protocol.SerializeNativeOpenConnection3Version6( request, commonInfo, Guid.Empty, [0x00, 0x00, 0x00, 0x00]); int hostLengthOffset = 18; int credentialLengthOffset = hostLengthOffset + 4 + Encoding.Unicode.GetByteCount("H"); Assert.Equal([0x04, 0x00], actual[credentialLengthOffset..(credentialLengthOffset + 2)]); Assert.Equal([0x00, 0x00, 0x00, 0x00], actual[(credentialLengthOffset + 2)..(credentialLengthOffset + 6)]); } }