feat(historian-gateway): IHistorianProvisioning + GatewayTagProvisioner (EnsureTags, non-blocking)
Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
This commit is contained in:
+114
@@ -0,0 +1,114 @@
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.HistorianGateway.Contracts.Grpc;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests;
|
||||
|
||||
public sealed class GatewayTagProvisionerTests
|
||||
{
|
||||
private static GatewayTagProvisioner Provisioner(FakeHistorianGatewayClient fake) =>
|
||||
new(fake, NullLogger<GatewayTagProvisioner>.Instance);
|
||||
|
||||
[Fact]
|
||||
public async Task Ensures_numeric_tags_with_mapped_type()
|
||||
{
|
||||
var fake = new FakeHistorianGatewayClient { EnsureTagsResult = new TagOperationResults() };
|
||||
var p = Provisioner(fake);
|
||||
var reqs = new[]
|
||||
{
|
||||
new HistorianTagProvisionRequest("Pump1.Temp", DriverDataType.Float32, "degC", "Temp"),
|
||||
new HistorianTagProvisionRequest("Pump1.Run", DriverDataType.Boolean, null, null),
|
||||
};
|
||||
|
||||
var result = await p.EnsureTagsAsync(reqs, TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.NotNull(fake.LastEnsureDefinitions);
|
||||
Assert.Equal(2, fake.LastEnsureDefinitions!.Count);
|
||||
Assert.Equal(HistorianDataType.Float, fake.LastEnsureDefinitions[0].DataType);
|
||||
Assert.Equal(HistorianDataType.Int1, fake.LastEnsureDefinitions[1].DataType);
|
||||
Assert.Equal(2, result.Requested);
|
||||
Assert.Equal(0, result.Skipped);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Maps_metadata_and_coalesces_null_metadata_to_empty()
|
||||
{
|
||||
var fake = new FakeHistorianGatewayClient { EnsureTagsResult = new TagOperationResults() };
|
||||
var p = Provisioner(fake);
|
||||
|
||||
await p.EnsureTagsAsync(
|
||||
new[]
|
||||
{
|
||||
new HistorianTagProvisionRequest("Pump1.Temp", DriverDataType.Float32, "degC", "Temp"),
|
||||
new HistorianTagProvisionRequest("Pump1.Run", DriverDataType.Boolean, null, null),
|
||||
},
|
||||
TestContext.Current.CancellationToken);
|
||||
|
||||
var defs = fake.LastEnsureDefinitions!;
|
||||
Assert.Equal("Pump1.Temp", defs[0].TagName);
|
||||
Assert.Equal("degC", defs[0].EngineeringUnit);
|
||||
Assert.Equal("Temp", defs[0].Description);
|
||||
// Proto string fields are non-nullable — null metadata must coalesce to empty.
|
||||
Assert.Equal(string.Empty, defs[1].EngineeringUnit);
|
||||
Assert.Equal(string.Empty, defs[1].Description);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Deferred_types_are_skipped_not_sent()
|
||||
{
|
||||
var fake = new FakeHistorianGatewayClient { EnsureTagsResult = new TagOperationResults() };
|
||||
var p = Provisioner(fake);
|
||||
|
||||
var result = await p.EnsureTagsAsync(
|
||||
new[] { new HistorianTagProvisionRequest("Pump1.Name", DriverDataType.String, null, null) },
|
||||
TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.Empty(fake.LastEnsureDefinitions!); // String is deferred → never built into a definition
|
||||
Assert.Equal(1, result.Requested);
|
||||
Assert.Equal(1, result.Skipped);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Gateway_failure_is_swallowed_and_counted_not_thrown()
|
||||
{
|
||||
var fake = new FakeHistorianGatewayClient { EnsureTagsThrows = new Exception("boom") };
|
||||
var p = Provisioner(fake);
|
||||
|
||||
var result = await p.EnsureTagsAsync(
|
||||
new[] { new HistorianTagProvisionRequest("Pump1.Temp", DriverDataType.Float32, null, null) },
|
||||
TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.Equal(1, result.Failed); // non-blocking: no throw
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Ensured_count_reflects_successful_results()
|
||||
{
|
||||
var fake = new FakeHistorianGatewayClient
|
||||
{
|
||||
EnsureTagsResult = new TagOperationResults
|
||||
{
|
||||
Results =
|
||||
{
|
||||
new TagOperationResult { Name = "Pump1.Temp", Success = true },
|
||||
new TagOperationResult { Name = "Pump1.Run", Success = false, Error = "x" },
|
||||
},
|
||||
},
|
||||
};
|
||||
var p = Provisioner(fake);
|
||||
|
||||
var result = await p.EnsureTagsAsync(
|
||||
new[]
|
||||
{
|
||||
new HistorianTagProvisionRequest("Pump1.Temp", DriverDataType.Float32, null, null),
|
||||
new HistorianTagProvisionRequest("Pump1.Run", DriverDataType.Boolean, null, null),
|
||||
},
|
||||
TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.Equal(2, result.Requested);
|
||||
Assert.Equal(1, result.Ensured);
|
||||
Assert.Equal(0, result.Skipped);
|
||||
Assert.Equal(1, result.Failed);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user