81aff03748
Browse mirrors ReadRawAsync (collect-then-yield); metadata mirrors ReadAtTimeAsync (unary). Delegates to the HistorianGrpcTagClient …OnSession seams so a leased session browses + reads metadata without re-handshaking (pending.md A1 broadening). Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
103 lines
4.6 KiB
C#
103 lines
4.6 KiB
C#
using AVEVA.Historian.Client.Models;
|
|
using Xunit.Abstractions;
|
|
|
|
namespace AVEVA.Historian.Client.Tests;
|
|
|
|
/// <summary>
|
|
/// Live end-to-end round-trip for <see cref="HistorianSession"/>: open → write → read → status →
|
|
/// ping → dispose — ALL on ONE reused session (no re-handshake). Env-gated exactly like
|
|
/// <see cref="HandshakeReuseSpikeTests"/> (silent skip without HISTORIAN_GRPC_HOST +
|
|
/// HISTORIAN_WRITE_SANDBOX_TAG + HISTORIAN_USER). Bounded write to the sandbox tag only.
|
|
/// </summary>
|
|
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<HistorianSample>();
|
|
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");
|
|
|
|
// 5) metadata + browse on the SAME session (no re-handshake)
|
|
HistorianTagMetadata? meta = await session.GetTagMetadataAsync(sandboxTag, CancellationToken.None);
|
|
Assert.NotNull(meta);
|
|
_output.WriteLine("5) metadata -> ok");
|
|
|
|
List<string> browsed = [];
|
|
await foreach (string n in session.BrowseTagNamesAsync("*", CancellationToken.None))
|
|
{
|
|
browsed.Add(n);
|
|
if (browsed.Count >= 5) break;
|
|
}
|
|
Assert.NotEmpty(browsed);
|
|
_output.WriteLine($"6) browse rows={browsed.Count}");
|
|
|
|
_output.WriteLine("session round-trip OK (write+read+status+ping+metadata+browse 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
|
|
};
|
|
}
|
|
}
|