f7f7d948cd
DefaultSessionAdapter swallowed a Bad HistoryReadResult.StatusCode (raw: break; aggregate: skip) so Client.CLI/UI printed an empty result for BadHistoryOperationUnsupported / BadUserAccessDenied. It now throws a ServiceResultException naming the node and status; BadNoData stays empty. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
39 lines
1.5 KiB
C#
39 lines
1.5 KiB
C#
using Opc.Ua;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Client.Shared.Helpers;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Client.Shared.Tests.Helpers;
|
|
|
|
public class HistoryReadStatusTests
|
|
{
|
|
/// <summary>A refused read must surface, carrying the status code, rather than read as an empty result.</summary>
|
|
/// <param name="code">The Bad per-node status code.</param>
|
|
[Theory]
|
|
[InlineData(StatusCodes.BadHistoryOperationUnsupported)]
|
|
[InlineData(StatusCodes.BadUserAccessDenied)]
|
|
[InlineData(StatusCodes.BadNodeIdUnknown)]
|
|
[InlineData(StatusCodes.BadTimeout)]
|
|
public void Bad_status_throws_ServiceResultException_with_the_code(uint code)
|
|
{
|
|
var ex = Should.Throw<ServiceResultException>(
|
|
() => HistoryReadStatus.ThrowIfRefused(new StatusCode(code), new NodeId("n", 2)));
|
|
|
|
ex.StatusCode.ShouldBe(code);
|
|
ex.Message.ShouldContain("ns=2;s=n");
|
|
}
|
|
|
|
/// <summary>Good, GoodNoData, Uncertain and BadNoData are all non-refusals (the window may simply be empty).</summary>
|
|
/// <param name="code">The non-refusal status code.</param>
|
|
[Theory]
|
|
[InlineData(StatusCodes.Good)]
|
|
[InlineData(StatusCodes.GoodNoData)]
|
|
[InlineData(StatusCodes.GoodMoreData)]
|
|
[InlineData(StatusCodes.UncertainNoCommunicationLastUsableValue)]
|
|
[InlineData(StatusCodes.BadNoData)]
|
|
public void Non_refusal_status_does_not_throw(uint code)
|
|
{
|
|
Should.NotThrow(() => HistoryReadStatus.ThrowIfRefused(new StatusCode(code), new NodeId("n", 2)));
|
|
}
|
|
}
|