Files
lmxopcua/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests/Mapping/AggregateModeMapperTests.cs
T
Joseph Doherty 6ea3ba44da feat(historian): advertise only the backend's aggregates under AggregateFunctions (#682)
IHistorianDataSource.SupportedAggregates (default: every HistoryAggregateType)
is the one authority for both ServerCapabilities/AggregateFunctions (i=2997) and
HistoryServerCapabilities/AggregateFunctions (i=11201) and for the up-front
BadAggregateNotSupported in HistoryRead-Processed. Gateway declares
AggregateModeMapper.Served (4), SqlServer/QuestDB AggregateFamilies.Served (17),
Null none. Only the folder references are trimmed; the SDK's calculator
factories stay so live AggregateFilter subscriptions keep working.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-12 08:52:47 -04:00

60 lines
3.0 KiB
C#

using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions; // HistoryAggregateType
using ZB.MOM.WW.HistorianGateway.Contracts.Grpc; // RetrievalMode
using ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Mapping;
namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway.Tests.Mapping;
public sealed class AggregateModeMapperTests
{
[Theory]
[InlineData(HistoryAggregateType.TimeAverage, RetrievalMode.TimeWeightedAverage)]
[InlineData(HistoryAggregateType.Minimum, RetrievalMode.MinimumWithTime)]
[InlineData(HistoryAggregateType.Maximum, RetrievalMode.MaximumWithTime)]
[InlineData(HistoryAggregateType.Total, RetrievalMode.Integral)]
public void Maps_each_aggregate(HistoryAggregateType a, RetrievalMode expected)
=> Assert.Equal(expected, AggregateModeMapper.ToRetrievalMode(a));
[Fact] // matrix guard: every member is either mapped or explicitly reported as not supported
public void Every_aggregate_member_is_mapped_or_reports_not_supported()
{
var original = new[]
{
HistoryAggregateType.Minimum, HistoryAggregateType.Maximum,
HistoryAggregateType.Total, HistoryAggregateType.TimeAverage,
};
foreach (var a in Enum.GetValues<HistoryAggregateType>())
{
if (original.Contains(a))
_ = AggregateModeMapper.ToRetrievalMode(a);
else
Assert.Throws<HistoryAggregateNotSupportedException>(() => AggregateModeMapper.ToRetrievalMode(a));
}
}
[Theory] // #679: AVEVA has no arithmetic mean and no sample count (Counter is a rollover increase)
[InlineData(HistoryAggregateType.Average)]
[InlineData(HistoryAggregateType.Count)]
public void Average_and_Count_are_not_supported(HistoryAggregateType a)
=> Assert.Throws<HistoryAggregateNotSupportedException>(() => AggregateModeMapper.ToRetrievalMode(a));
[Fact] // #682: the served (= advertised) set is exactly what the mapper maps, and the data source returns it
public void Served_is_exactly_the_mapped_members_and_the_data_source_declares_it()
{
Assert.Equal(
new[] { HistoryAggregateType.Minimum, HistoryAggregateType.Maximum, HistoryAggregateType.Total, HistoryAggregateType.TimeAverage }
.OrderBy(a => a),
AggregateModeMapper.Served.OrderBy(a => a));
foreach (var a in Enum.GetValues<HistoryAggregateType>().Where(a => !AggregateModeMapper.Served.Contains(a)))
Assert.Throws<HistoryAggregateNotSupportedException>(() => AggregateModeMapper.ToRetrievalMode(a));
var source = new GatewayHistorianDataSource(
new FakeHistorianGatewayClient(), Microsoft.Extensions.Logging.Abstractions.NullLogger<GatewayHistorianDataSource>.Instance);
Assert.Same(AggregateModeMapper.Served, source.SupportedAggregates);
}
[Fact]
public void Undefined_value_is_a_programming_error_not_a_not_supported()
=> Assert.Throws<ArgumentOutOfRangeException>(() => AggregateModeMapper.ToRetrievalMode((HistoryAggregateType)9999));
}