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); } }