test(grpc): live + gated coverage for the gRPC config ops
- GetRuntimeParameterAsync_OverGrpc_ReturnsValue (live) - GetTagExtendedPropertiesAsync_OverGrpc_DoesNotThrow (live; empty for system tags) - ExecuteSqlCommandAsync_OverGrpc_IsServerWalled (live; pins the captured wall) - TagWriteLifecycle_OverGrpc_CreatesAddsPropRenamesDeletes — DESTRUCTIVE, gated on HISTORIAN_GRPC_WRITE_SANDBOX_TAG; self-cleaning create->addprop->verify->rename->delete Full gRPC live suite 19/19 green against a real 2023 R2 server (write lifecycle skips without the sandbox tag); 317 offline green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B6mcaT2PjRFKcogzp9UkfC
This commit is contained in:
@@ -351,6 +351,104 @@ public sealed class HistorianGrpcIntegrationTests
|
||||
Assert.All(samples, s => Assert.Equal(testTag, s.TagName));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetRuntimeParameterAsync_OverGrpc_ReturnsValue()
|
||||
{
|
||||
string? host = Environment.GetEnvironmentVariable("HISTORIAN_GRPC_HOST");
|
||||
if (string.IsNullOrWhiteSpace(host) || string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HISTORIAN_USER")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Config op tooled over gRPC: StatusService.GetRuntimeParameter carries the proven 2020 GETRP
|
||||
// request/response buffers unchanged inside the protobuf bytes fields.
|
||||
HistorianClient client = new(BuildOptions(host));
|
||||
string? value = await client.GetRuntimeParameterAsync("HistorianVersion", CancellationToken.None);
|
||||
Assert.False(string.IsNullOrWhiteSpace(value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetTagExtendedPropertiesAsync_OverGrpc_DoesNotThrow()
|
||||
{
|
||||
string? host = Environment.GetEnvironmentVariable("HISTORIAN_GRPC_HOST");
|
||||
string? tag = Environment.GetEnvironmentVariable("HISTORIAN_TEST_TAG");
|
||||
if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(tag)
|
||||
|| string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HISTORIAN_USER")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Config op tooled over gRPC: RetrievalService.GetTagExtendedPropertiesFromName carries the
|
||||
// proven 2020 GetTepByNm buffers. A system tag may have no user-defined properties, so this
|
||||
// asserts the call completes and returns a well-formed (possibly empty) list.
|
||||
HistorianClient client = new(BuildOptions(host));
|
||||
IReadOnlyList<HistorianTagExtendedProperty> props = await client.GetTagExtendedPropertiesAsync(tag!, CancellationToken.None);
|
||||
Assert.NotNull(props);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExecuteSqlCommandAsync_OverGrpc_IsServerWalled()
|
||||
{
|
||||
string? host = Environment.GetEnvironmentVariable("HISTORIAN_GRPC_HOST");
|
||||
if (string.IsNullOrWhiteSpace(host) || string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HISTORIAN_USER")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// ExecuteSqlCommand request rides the gRPC front door, but the server-side
|
||||
// CSrvDbConnection.ExecuteSqlCommand faults (IndexOutOfRange / native error 38) — an unmet
|
||||
// DB-connection precondition the pure managed gRPC session doesn't establish (captured
|
||||
// 2026-06-22). The SDK surfaces this as ProtocolEvidenceMissingException. This test pins the
|
||||
// wall so a future server/registration change that lifts it is noticed.
|
||||
HistorianClient client = new(BuildOptions(host));
|
||||
await Assert.ThrowsAsync<ProtocolEvidenceMissingException>(() => client.ExecuteSqlCommandAsync(
|
||||
"SELECT 10 AS Num, 'alpha' AS Word UNION ALL SELECT 20, NULL",
|
||||
cancellationToken: CancellationToken.None));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TagWriteLifecycle_OverGrpc_CreatesAddsPropRenamesDeletes()
|
||||
{
|
||||
string? host = Environment.GetEnvironmentVariable("HISTORIAN_GRPC_HOST");
|
||||
// DESTRUCTIVE: gated on a dedicated sandbox-tag name so it never mutates a server by accident.
|
||||
// Set HISTORIAN_GRPC_WRITE_SANDBOX_TAG to a throwaway tag name the test may create/rename/delete.
|
||||
string? sandbox = Environment.GetEnvironmentVariable("HISTORIAN_GRPC_WRITE_SANDBOX_TAG");
|
||||
if (string.IsNullOrWhiteSpace(host) || string.IsNullOrWhiteSpace(sandbox)
|
||||
|| string.IsNullOrEmpty(Environment.GetEnvironmentVariable("HISTORIAN_USER")))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Exercises the full gRPC tag-config write surface end-to-end against a write-enabled (0x401)
|
||||
// session, then cleans up after itself: EnsureTags -> AddTagExtendedProperties ->
|
||||
// (read-back verify) -> StartJob rename -> DeleteTags.
|
||||
HistorianClient client = new(BuildOptions(host));
|
||||
string renamed = sandbox + "_R";
|
||||
|
||||
try
|
||||
{
|
||||
bool created = await client.EnsureTagAsync(
|
||||
new HistorianTagDefinition { TagName = sandbox!, DataType = HistorianDataType.Float, EngineeringUnit = "u", MaxEU = 100 },
|
||||
CancellationToken.None);
|
||||
Assert.True(created, "EnsureTags over gRPC should create the sandbox tag.");
|
||||
|
||||
bool propAdded = await client.AddTagExtendedPropertyAsync(sandbox!, "GrpcToolingTest", "ok", CancellationToken.None);
|
||||
Assert.True(propAdded, "AddTagExtendedProperties over gRPC should succeed.");
|
||||
|
||||
IReadOnlyList<HistorianTagExtendedProperty> props = await client.GetTagExtendedPropertiesAsync(sandbox!, CancellationToken.None);
|
||||
Assert.Contains(props, p => string.Equals(p.Name, "GrpcToolingTest", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
HistorianTagRenameResult rename = await client.RenameTagsAsync([(sandbox!, renamed)], CancellationToken.None);
|
||||
Assert.True(rename.Accepted, $"StartJob rename over gRPC should be accepted: {rename.Error}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Best-effort cleanup of whichever name survives (rename is an async server job).
|
||||
try { await client.DeleteTagAsync(sandbox!, CancellationToken.None); } catch { /* ignore */ }
|
||||
try { await client.DeleteTagAsync(renamed, CancellationToken.None); } catch { /* ignore */ }
|
||||
}
|
||||
}
|
||||
|
||||
private static HistorianClientOptions BuildOptions(string host)
|
||||
{
|
||||
string? user = Environment.GetEnvironmentVariable("HISTORIAN_USER");
|
||||
|
||||
Reference in New Issue
Block a user