1e93b2ebfb
Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
76 lines
3.6 KiB
C#
76 lines
3.6 KiB
C#
using Google.Protobuf.WellKnownTypes;
|
|
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 GatewayHistorianDataSourceTests
|
|
{
|
|
[Fact]
|
|
public async Task ReadRaw_maps_samples_and_passes_args()
|
|
{
|
|
var fake = new FakeHistorianGatewayClient
|
|
{
|
|
RawSamples = new[]
|
|
{
|
|
new HistorianSample { Tag = "T", NumericValue = 1.0, OpcQuality = 192, Timestamp = Ts(2026, 1, 1, 0, 0, 0) },
|
|
new HistorianSample { Tag = "T", NumericValue = 2.0, OpcQuality = 0, Timestamp = Ts(2026, 1, 1, 0, 0, 1) },
|
|
},
|
|
};
|
|
var ds = new GatewayHistorianDataSource(fake, NullLogger<GatewayHistorianDataSource>.Instance);
|
|
var r = await ds.ReadRawAsync("T", DateTime.UtcNow.AddMinutes(-5), DateTime.UtcNow, 100, TestContext.Current.CancellationToken);
|
|
Assert.Equal(2, r.Samples.Count);
|
|
Assert.Equal(0x80000000u, r.Samples[1].StatusCode); // Bad from quality 0
|
|
Assert.Equal("T", fake.LastReadRawTag);
|
|
Assert.Equal(100, fake.LastReadRawMaxValues);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReadProcessed_uses_aggregate_mode_mapping()
|
|
{
|
|
var fake = new FakeHistorianGatewayClient();
|
|
var ds = new GatewayHistorianDataSource(fake, NullLogger<GatewayHistorianDataSource>.Instance);
|
|
await ds.ReadProcessedAsync("T", default, default, TimeSpan.FromSeconds(60), HistoryAggregateType.Minimum, TestContext.Current.CancellationToken);
|
|
Assert.Equal(RetrievalMode.MinimumWithTime, fake.LastAggregateMode);
|
|
Assert.Equal(TimeSpan.FromSeconds(60), fake.LastAggregateInterval);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReadAtTime_aligns_one_snapshot_per_timestamp_with_gaps_Bad()
|
|
{
|
|
var fake = new FakeHistorianGatewayClient();
|
|
var t0 = new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc);
|
|
var t1 = t0.AddSeconds(1);
|
|
fake.AtTimeSamples = new[] { new HistorianSample { NumericValue = 5.0, OpcQuality = 192, Timestamp = Timestamp.FromDateTime(t0) } };
|
|
var ds = new GatewayHistorianDataSource(fake, NullLogger<GatewayHistorianDataSource>.Instance);
|
|
var r = await ds.ReadAtTimeAsync("T", new[] { t0, t1 }, TestContext.Current.CancellationToken);
|
|
Assert.Equal(2, r.Samples.Count); // exactly one per requested ts, in order
|
|
Assert.Equal(5.0, r.Samples[0].Value);
|
|
Assert.Equal(0x80000000u, r.Samples[1].StatusCode); // gap → Bad at requested ts
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Empty_window_is_not_a_fault()
|
|
{
|
|
var fake = new FakeHistorianGatewayClient { RawSamples = Array.Empty<HistorianSample>() };
|
|
var ds = new GatewayHistorianDataSource(fake, NullLogger<GatewayHistorianDataSource>.Instance);
|
|
var r = await ds.ReadRawAsync("T", default, default, 10, TestContext.Current.CancellationToken);
|
|
Assert.Empty(r.Samples); // GoodNoData-empty, no throw
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Disposing_data_source_disposes_client()
|
|
{
|
|
var fake = new FakeHistorianGatewayClient();
|
|
var ds = new GatewayHistorianDataSource(fake, NullLogger<GatewayHistorianDataSource>.Instance);
|
|
await ds.DisposeAsync();
|
|
Assert.Equal(1, fake.DisposeCallCount);
|
|
}
|
|
|
|
// Ts(...) builds a Google.Protobuf.WellKnownTypes.Timestamp from UTC parts.
|
|
private static Timestamp Ts(int y, int mo, int d, int h, int mi, int s)
|
|
=> Timestamp.FromDateTime(new DateTime(y, mo, d, h, mi, s, DateTimeKind.Utc));
|
|
}
|