Files
histsdk/tests/AVEVA.Historian.Client.Tests/GrpcInterfaceVersionEvidenceTests.cs
T

104 lines
5.2 KiB
C#

using AVEVA.Historian.Client.Grpc;
using GrpcHistory = ArchestrA.Grpc.Contract.History;
using GrpcRetrieval = ArchestrA.Grpc.Contract.Retrieval;
using GrpcStatus = ArchestrA.Grpc.Contract.Status;
using GrpcTransaction = ArchestrA.Grpc.Contract.Transaction;
using Xunit.Abstractions;
namespace AVEVA.Historian.Client.Tests;
/// <summary>
/// Live evidence test (C3a): reads the four unauthenticated <c>GetInterfaceVersion</c> RPCs from a
/// real 2023 R2 Historian over gRPC and asserts the accepted version set. These RPCs run before any
/// credential exchange, so no HISTORIAN_USER / HISTORIAN_PASSWORD is required — only a reachable host.
///
/// Skips silently when <c>HISTORIAN_GRPC_HOST</c> is absent (offline / CI). The captured integers
/// are recorded in <c>docs/reverse-engineering/grpc-interface-versions.md</c> and close the C3a
/// "2023 R2 gRPC server-version integers not yet captured" gap.
/// </summary>
public sealed class GrpcInterfaceVersionEvidenceTests
{
private readonly ITestOutputHelper _output;
public GrpcInterfaceVersionEvidenceTests(ITestOutputHelper output) => _output = output;
[Fact]
public void GrpcInterfaceVersions_LiveServer_MatchAcceptedSet()
{
string host = Environment.GetEnvironmentVariable("HISTORIAN_GRPC_HOST") ?? "";
if (string.IsNullOrWhiteSpace(host))
{
_output.WriteLine("SKIP: HISTORIAN_GRPC_HOST is not set — no live historian available.");
return;
}
HistorianClientOptions options = BuildOptions(host);
using HistorianGrpcConnection connection = HistorianGrpcChannelFactory.Create(options);
DateTime deadline = DateTime.UtcNow.Add(TimeSpan.FromSeconds(10));
GrpcHistory.GetInterfaceVersionResponse history =
new GrpcHistory.HistoryService.HistoryServiceClient(connection.Channel)
.GetInterfaceVersion(new GrpcHistory.GetInterfaceVersionRequest(), connection.Metadata, deadline, default);
GrpcRetrieval.GetRetrievalInterfaceVersionResponse retrieval =
new GrpcRetrieval.RetrievalService.RetrievalServiceClient(connection.Channel)
.GetRetrievalInterfaceVersion(new GrpcRetrieval.GetRetrievalInterfaceVersionRequest(), connection.Metadata, deadline, default);
GrpcStatus.GetStatusInterfaceVersionResponse status =
new GrpcStatus.StatusService.StatusServiceClient(connection.Channel)
.GetStatusInterfaceVersion(new GrpcStatus.GetStatusInterfaceVersionRequest(), connection.Metadata, deadline, default);
GrpcTransaction.GetTransactionInterfaceVersionResponse transaction =
new GrpcTransaction.TransactionService.TransactionServiceClient(connection.Channel)
.GetTransactionInterfaceVersion(new GrpcTransaction.GetTransactionInterfaceVersionRequest(), connection.Metadata, deadline, default);
_output.WriteLine($"History UiVersion={history.UiVersion} UiError={history.UiError}");
_output.WriteLine($"Retrieval UiVersion={retrieval.UiVersion} UiError={retrieval.UiError}");
_output.WriteLine($"Status UiVersion={status.UiVersion} UiError={status.UiError}");
// Note: Transaction response fields are named Error/Version (not UiError/UiVersion) per the proto.
_output.WriteLine($"Transaction Version={transaction.Version} Error={transaction.Error}");
// History: accepted set is {11 (2020 WCF), 12 (2023 R2 gRPC)}.
Assert.Equal(0u, history.UiError);
Assert.Contains(history.UiVersion, new uint[] { 11u, 12u });
// Retrieval: 4 on both 2020 WCF and 2023 R2 gRPC.
Assert.Equal(0u, retrieval.UiError);
Assert.Equal(4u, retrieval.UiVersion);
// Transaction: 2 (confirmed by live gRPC capture — see grpc-interface-versions.md).
// NOTE: the Transaction response proto uses Error/Version (not UiError/UiVersion).
Assert.Equal(0u, transaction.Error);
Assert.Equal(2u, transaction.Version);
// Status: reachability-only — assert UiError==0 only; UiVersion is not value-gated
// (observed 4 on 2023 R2 gRPC, 0 on 2020 WCF).
Assert.Equal(0u, status.UiError);
}
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);
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,
};
}
}