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 }; } }