From be60d0b8d943b6f33cb376bd9c73651dc82bb647 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 25 Jun 2026 03:09:18 -0400 Subject: [PATCH] test: HistorianSession end-to-end round-trip (write+read+status+ping on one session, env-gated) Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii --- .../HistorianSessionRoundTripTests.cs | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 tests/AVEVA.Historian.Client.Tests/HistorianSessionRoundTripTests.cs diff --git a/tests/AVEVA.Historian.Client.Tests/HistorianSessionRoundTripTests.cs b/tests/AVEVA.Historian.Client.Tests/HistorianSessionRoundTripTests.cs new file mode 100644 index 0000000..1cfce4d --- /dev/null +++ b/tests/AVEVA.Historian.Client.Tests/HistorianSessionRoundTripTests.cs @@ -0,0 +1,88 @@ +using AVEVA.Historian.Client.Models; +using Xunit.Abstractions; + +namespace AVEVA.Historian.Client.Tests; + +/// +/// Live end-to-end round-trip for : open → write → read → status → +/// ping → dispose — ALL on ONE reused session (no re-handshake). Env-gated exactly like +/// (silent skip without HISTORIAN_GRPC_HOST + +/// HISTORIAN_WRITE_SANDBOX_TAG + HISTORIAN_USER). Bounded write to the sandbox tag only. +/// +public sealed class HistorianSessionRoundTripTests +{ + private readonly ITestOutputHelper _output; + + public HistorianSessionRoundTripTests(ITestOutputHelper output) => _output = output; + + [Fact] + public async Task Session_WriteReadStatusPing_AllOnOneSession() + { + string? host = Environment.GetEnvironmentVariable("HISTORIAN_GRPC_HOST"); + string? sandboxTag = Environment.GetEnvironmentVariable("HISTORIAN_WRITE_SANDBOX_TAG"); + if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(sandboxTag) + || string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HISTORIAN_USER"))) + { + return; // skip — env not configured + } + + HistorianClientOptions options = BuildOptions(host); + await using var client = new HistorianClient(options); + await using HistorianSession session = await client.OpenSessionAsync(HistorianSessionKind.WriteEnabled, CancellationToken.None); + + // 1) write on the session + bool wrote = await session.AddHistoricalValuesAsync(sandboxTag, + new[] { new HistorianHistoricalValue(DateTime.UtcNow, 42.0, OpcQuality: 192) }, + CancellationToken.None); + Assert.True(wrote); + _output.WriteLine("1) write -> ok"); + + // 2) read on the SAME session + DateTime end = DateTime.UtcNow, start = end - TimeSpan.FromDays(7); + var rows = new List(); + await foreach (HistorianSample s in session.ReadRawAsync(sandboxTag, start, end, 8, CancellationToken.None)) + rows.Add(s); + _output.WriteLine($"2) session read rows={rows.Count}"); + Assert.NotEmpty(rows); + + // 3) status + ping on the SAME session + string? version = await session.GetSystemParameterAsync("HistorianVersion", CancellationToken.None); + Assert.False(string.IsNullOrWhiteSpace(version)); + _output.WriteLine($"3) HistorianVersion={version}"); + + await session.PingAsync(CancellationToken.None); // must not throw + _output.WriteLine("4) ping -> ok"); + + _output.WriteLine("session round-trip OK (write+read+status+ping on one session)"); + } + + // verbatim copy of BuildOptions from HandshakeReuseSpikeTests + private static HistorianClientOptions BuildOptions(string host) + { + string? user = Environment.GetEnvironmentVariable("HISTORIAN_USER"); + string? password = Environment.GetEnvironmentVariable("HISTORIAN_PASSWORD"); + bool explicitCreds = !string.IsNullOrEmpty(user); + int port = int.TryParse(Environment.GetEnvironmentVariable("HISTORIAN_GRPC_PORT"), out int parsed) + ? parsed + : HistorianClientOptions.DefaultGrpcPort; + bool tls = string.Equals(Environment.GetEnvironmentVariable("HISTORIAN_GRPC_TLS"), "true", StringComparison.OrdinalIgnoreCase); + TimeSpan timeout = int.TryParse(Environment.GetEnvironmentVariable("HISTORIAN_GRPC_TIMEOUT"), out int secs) && secs > 0 + ? TimeSpan.FromSeconds(secs) + : new HistorianClientOptions { Host = host }.RequestTimeout; + + return new HistorianClientOptions + { + Host = host, + Port = port, + Transport = HistorianTransport.RemoteGrpc, + GrpcUseTls = tls, + AllowUntrustedServerCertificate = tls, + ServerDnsIdentity = Environment.GetEnvironmentVariable("HISTORIAN_GRPC_DNSID"), + IntegratedSecurity = !explicitCreds, + UserName = user ?? string.Empty, + Password = password ?? string.Empty, + RequestTimeout = timeout, + Compression = true + }; + } +}