review(Core.Abstractions): document ReadEventsAsync continuation contract (OpcUaServer-002 root)

Re-review at 7286d320. Core.Abstractions-009: ReadEventsAsync maxEvents<=0 sentinel now
documents the implementer's continuation-point obligation when a backend cap truncates
(the root of OpcUaServer-002). -010: PollGroupEngineTests pass CancellationToken. Plus
EquipmentTagRefResolver.TryResolve [MaybeNullWhen(false)] NRT cleanup + test.
This commit is contained in:
Joseph Doherty
2026-06-19 11:06:56 -04:00
parent 354b0e7613
commit 65e6af6001
7 changed files with 135 additions and 12 deletions
@@ -169,6 +169,33 @@ public sealed class IHistorianDataSourceContractTests
parameter!.ParameterType.ShouldBe(expectedType);
}
/// <summary>
/// Core.Abstractions-009 (OpcUaServer-002): when <c>maxEvents &lt;= 0</c> the contract
/// requires implementations to set <see cref="HistoricalEventsResult.ContinuationPoint"/>
/// non-null when more results exist. Pins the <c>maxEvents</c> parameter type and the
/// <see cref="HistoricalEventsResult"/> continuation-point member so accidental removal
/// breaks this test — the downstream paging contract depends on both.
/// </summary>
[Fact]
public void ReadEventsAsync_sentinel_and_continuation_contract_types_pinned()
{
var method = typeof(IHistorianDataSource).GetMethod("ReadEventsAsync");
method.ShouldNotBeNull();
var maxEventsParam = method!.GetParameters().FirstOrDefault(p => p.Name == "maxEvents");
maxEventsParam.ShouldNotBeNull("maxEvents parameter must exist for the sentinel contract");
maxEventsParam!.ParameterType.ShouldBe(typeof(int),
"maxEvents is int (not uint) so callers can pass <=0 as a 'use backend default cap' sentinel");
// HistoricalEventsResult must carry a nullable ContinuationPoint so implementations
// can signal 'more results exist' (non-null) vs 'all results returned' (null).
var resultType = typeof(HistoricalEventsResult);
var continuationProp = resultType.GetProperty("ContinuationPoint");
continuationProp.ShouldNotBeNull("HistoricalEventsResult.ContinuationPoint must exist for paging");
continuationProp!.PropertyType.ShouldBe(typeof(byte[]),
"ContinuationPoint is byte[]? — null means no more pages, non-null means caller must page");
}
/// <summary>
/// Core.Abstractions-008: <see cref="IHistoryProvider.ReadAtTimeAsync"/> and
/// <see cref="IHistoryProvider.ReadEventsAsync"/> are C# default interface methods