using System.Text.Json;
using Moq;
using Opc.Ua;
using Opc.Ua.Client;
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
///
/// Gitea #764 — the SDK keeps sending publish requests on a session that holds no subscription, and the server
/// answers each with an anonymous (id 0) BadNoSubscription. With nothing tracked there is nothing to lose,
/// so those answers must not inflate the publish_error counter. Any other PublishError still counts.
///
[Trait("Category", "Unit")]
public sealed class OpcUaClientIdlePublishErrorTests
{
private const string SpeedRef = "Plant/dev/Speed";
private const string AlarmRef = "Plant/dev/TempHi";
private static readonly IReadOnlyList Tags =
[
new(SpeedRef, JsonSerializer.Serialize(new { nodeId = "ns=0;s=Speed" }), WriteIdempotent: true),
];
private static OpcUaClientDriver NewConnectedDriver(string instance)
{
var drv = new OpcUaClientDriver(new OpcUaClientDriverOptions { RawTags = Tags }, instance);
var session = new Mock(MockBehavior.Loose);
session.SetupGet(s => s.MessageContext).Returns(ServiceMessageContext.Create(SdkTelemetry.Context));
session.SetupGet(s => s.NamespaceUris).Returns(new NamespaceTable());
drv.SetSessionForTest(session.Object);
drv.SetNamespaceMapForTest(NamespaceMap.FromTable(new NamespaceTable(), Tags));
drv.SetHealthForTest(new DriverHealth(DriverState.Healthy, DateTime.UtcNow, null));
return drv;
}
[Fact]
public async Task An_anonymous_BadNoSubscription_on_an_idle_session_is_not_counted()
{
var instance = $"opcua-764-{Guid.NewGuid():N}";
using var cap = new OpcUaClientDiagnosticsTests.Capture(instance);
var drv = NewConnectedDriver(instance);
await using var _ = drv;
drv.RaisePublishErrorForTest(StatusCodes.BadNoSubscription);
drv.RaisePublishErrorForTest(StatusCodes.BadNoSubscription);
cap[OpcUaClientDiagnostics.PublishError].ShouldBe(0, "idle-session publish answers are not errors");
// Positive control: any other publish error on the same idle driver still counts.
drv.RaisePublishErrorForTest(StatusCodes.BadTooManyPublishRequests);
cap[OpcUaClientDiagnostics.PublishError].ShouldBe(1);
drv.GetHealth().State.ShouldBe(DriverState.Healthy);
}
[Fact]
public async Task An_anonymous_BadNoSubscription_with_a_tracked_subscription_is_still_counted()
{
var instance = $"opcua-764-{Guid.NewGuid():N}";
using var cap = new OpcUaClientDiagnosticsTests.Capture(instance);
var drv = NewConnectedDriver(instance);
await using var _ = drv;
drv.AddAlarmSubscriptionForTest([AlarmRef]);
drv.RaisePublishErrorForTest(StatusCodes.BadNoSubscription);
cap[OpcUaClientDiagnostics.PublishError].ShouldBe(1);
}
}