35aace7fdf
Claude-Session: https://claude.ai/code/session_012SDSQ3AcaXqPcBtDESBRii
82 lines
3.9 KiB
C#
82 lines
3.9 KiB
C#
using System.Linq;
|
|
using Google.Protobuf.WellKnownTypes;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Xunit;
|
|
using ZB.MOM.WW.HistorianGateway.Contracts.Grpc;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests;
|
|
|
|
public sealed class GatewayReadEventsTests
|
|
{
|
|
[Fact]
|
|
public async Task ReadEvents_maps_and_passes_source_filter()
|
|
{
|
|
var fake = new FakeHistorianGatewayClient
|
|
{
|
|
Events = new[]
|
|
{
|
|
new HistorianEvent { Id = "E1", SourceName = "Pump1", EventTime = Ts(2026, 1, 1, 0, 0, 0), ReceivedTime = Ts(2026, 1, 1, 0, 0, 0) },
|
|
},
|
|
};
|
|
var ds = new GatewayHistorianDataSource(fake, NullLogger<GatewayHistorianDataSource>.Instance);
|
|
var r = await ds.ReadEventsAsync("Pump1", DateTime.UtcNow.AddHours(-1), DateTime.UtcNow, maxEvents: 0, TestContext.Current.CancellationToken);
|
|
Assert.Single(r.Events);
|
|
Assert.Equal("E1", r.Events[0].EventId);
|
|
Assert.Equal("Pump1", fake.LastReadEventsSourceName);
|
|
Assert.Null(r.ContinuationPoint); // no cap → no truncation
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReadEvents_truncation_sets_continuation_point()
|
|
{
|
|
var fake = new FakeHistorianGatewayClient
|
|
{
|
|
Events = Enumerable.Range(0, 5)
|
|
.Select(i => new HistorianEvent { Id = $"E{i}", EventTime = Ts(2026, 1, 1, 0, 0, i), ReceivedTime = Ts(2026, 1, 1, 0, 0, i) })
|
|
.ToArray(),
|
|
};
|
|
var ds = new GatewayHistorianDataSource(fake, NullLogger<GatewayHistorianDataSource>.Instance);
|
|
var r = await ds.ReadEventsAsync(null, DateTime.UtcNow.AddHours(-1), DateTime.UtcNow, maxEvents: 3, TestContext.Current.CancellationToken);
|
|
Assert.Equal(3, r.Events.Count);
|
|
Assert.NotNull(r.ContinuationPoint); // cap truncated → non-null per Core.Abstractions-009
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReadEvents_cap_exactly_satisfied_has_no_continuation()
|
|
{
|
|
var fake = new FakeHistorianGatewayClient
|
|
{
|
|
Events = Enumerable.Range(0, 3)
|
|
.Select(i => new HistorianEvent { Id = $"E{i}", EventTime = Ts(2026, 1, 1, 0, 0, i), ReceivedTime = Ts(2026, 1, 1, 0, 0, i) })
|
|
.ToArray(),
|
|
};
|
|
var ds = new GatewayHistorianDataSource(fake, NullLogger<GatewayHistorianDataSource>.Instance);
|
|
var r = await ds.ReadEventsAsync(null, DateTime.UtcNow.AddHours(-1), DateTime.UtcNow, maxEvents: 3, TestContext.Current.CancellationToken);
|
|
Assert.Equal(3, r.Events.Count);
|
|
Assert.Null(r.ContinuationPoint); // exactly the cap, nothing beyond → no truncation
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ReadEvents_defensively_filters_by_source_name_client_side()
|
|
{
|
|
// Gateway returned a mixed window (source filter not yet applied server-side); the adapter
|
|
// must drop non-matching sources defensively.
|
|
var fake = new FakeHistorianGatewayClient
|
|
{
|
|
Events = new[]
|
|
{
|
|
new HistorianEvent { Id = "E1", SourceName = "Pump1", EventTime = Ts(2026, 1, 1, 0, 0, 0), ReceivedTime = Ts(2026, 1, 1, 0, 0, 0) },
|
|
new HistorianEvent { Id = "E2", SourceName = "Pump2", EventTime = Ts(2026, 1, 1, 0, 0, 1), ReceivedTime = Ts(2026, 1, 1, 0, 0, 1) },
|
|
new HistorianEvent { Id = "E3", SourceName = "Pump1", EventTime = Ts(2026, 1, 1, 0, 0, 2), ReceivedTime = Ts(2026, 1, 1, 0, 0, 2) },
|
|
},
|
|
};
|
|
var ds = new GatewayHistorianDataSource(fake, NullLogger<GatewayHistorianDataSource>.Instance);
|
|
var r = await ds.ReadEventsAsync("Pump1", DateTime.UtcNow.AddHours(-1), DateTime.UtcNow, maxEvents: 0, TestContext.Current.CancellationToken);
|
|
Assert.Equal(2, r.Events.Count);
|
|
Assert.All(r.Events, e => Assert.Equal("Pump1", e.SourceName));
|
|
}
|
|
|
|
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));
|
|
}
|