using AVEVA.Historian.Client.Models; using AVEVA.Historian.Client.Wcf; namespace AVEVA.Historian.Client.Tests; public sealed class WcfDataQueryResultBufferTests { // Captured from artifacts/reverse-engineering/instrumented-openconnection3-correlation/capture.ndjson // Wcf.GetNextQueryResultBuffer2.ResultBytes for a 4-row OtOpcUaParityTest_001.Counter Full read. private static readonly byte[] CapturedResultBytes = Convert.FromBase64String( "CQAEAAAA7gAAAB0AAABPAHQATwBwAGMAVQBhAFAAYQByAGkAdAB5AFQAZQBzAHQAXwAwADAAMQAu" + "AEMAbwB1AG4AdABlAHIAAQAAAGvPzFvD2dwBhQAAAPgAAADAAAAAAAAAAAAAAAAAAAAAAABZQAAA" + "AWvPzFvD2dwBAAAAAAAAAAClBtClfAAAAAAAAAAAAAAA7gAAAB0AAABPAHQATwBwAGMAVQBhAFAA" + "YQByAGkAdAB5AFQAZQBzAHQAXwAwADAAMQAuAEMAbwB1AG4AdABlAHIAAQAAABDWnAFA2twBAQAA" + "ABgAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAARDWnAFA2twBAAAAAAAAAAAwZOgAAAAAAAEAAAAA" + "AAAA7gAAAB0AAABPAHQATwBwAGMAVQBhAFAAYQByAGkAdAB5AFQAZQBzAHQAXwAwADAAMQAuAEMA" + "bwB1AG4AdABlAHIAAQAAAEA6hQJA2twBAQAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUA6" + "hQJA2twBAAAAAAAAAABQwwAAAAAAAAEAAAAAAAAA7gAAAB0AAABPAHQATwBwAGMAVQBhAFAAYQBy" + "AGkAdAB5AFQAZQBzAHQAXwAwADAAMQAuAEMAbwB1AG4AdABlAHIAAQAAAJD9hQJA2twBAAAAAPgA" + "AADAAAAAAAAAAAAAAAAAAAAAAABZQAAAAZD9hQJA2twBAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAA"); private static readonly byte[] TerminalNoMoreData = Convert.FromBase64String("BB4AAAA="); [Fact] public void TryParseGetNextQueryResultBufferRows_ParsesFourCanonicalFixtureRows() { bool ok = HistorianDataQueryProtocol.TryParseGetNextQueryResultBufferRows( CapturedResultBytes, TerminalNoMoreData, out IReadOnlyList rows, out bool hasMoreData); Assert.True(ok); Assert.False(hasMoreData); Assert.Equal(4, rows.Count); Assert.All(rows, r => Assert.Equal("OtOpcUaParityTest_001.Counter", r.TagName)); HistorianSample row0 = rows[0]; Assert.Equal(133, row0.Quality); Assert.Equal(248u, row0.QualityDetail); Assert.Equal(192, row0.OpcQuality); Assert.Equal(0, row0.NumericValue); Assert.Equal(100.0, row0.PercentGood); Assert.Equal(DateTime.FromFileTimeUtc(0x01DCD9C35BCCCF6B), row0.TimestampUtc); HistorianSample row3 = rows[3]; Assert.Equal(0, row3.Quality); Assert.Equal(248u, row3.QualityDetail); Assert.Equal(192, row3.OpcQuality); Assert.Equal(100.0, row3.PercentGood); } [Fact] public void TryParseGetNextQueryResultBufferRows_FlagsContinuationWhenErrorTerminalIsEmpty() { bool ok = HistorianDataQueryProtocol.TryParseGetNextQueryResultBufferRows( CapturedResultBytes, errorTerminal: [], out _, out bool hasMoreData); Assert.True(ok); Assert.True(hasMoreData); } [Fact] public void TryParseGetNextQueryResultBufferRows_FlagsContinuationWhenErrorIsNotNoMoreData() { bool ok = HistorianDataQueryProtocol.TryParseGetNextQueryResultBufferRows( CapturedResultBytes, errorTerminal: [0x04, 0x01, 0x00, 0x00, 0x00], out _, out bool hasMoreData); Assert.True(ok); Assert.True(hasMoreData); } [Fact] public void TryParseGetNextQueryResultBufferRows_RejectsBufferWithUnsupportedVersion() { byte[] mangled = (byte[])CapturedResultBytes.Clone(); mangled[0] = 0x07; bool ok = HistorianDataQueryProtocol.TryParseGetNextQueryResultBufferRows( mangled, TerminalNoMoreData, out IReadOnlyList rows, out _); Assert.False(ok); Assert.Empty(rows); } [Fact] public void TryParseGetNextQueryResultBufferRows_HandlesEmptyResultBuffer() { bool ok = HistorianDataQueryProtocol.TryParseGetNextQueryResultBufferRows( result: [], TerminalNoMoreData, out IReadOnlyList rows, out bool hasMoreData); Assert.True(ok); Assert.False(hasMoreData); Assert.Empty(rows); } }