Files
lmxopcua/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/DeferredServiceLevelPublisherTests.cs
Joseph Doherty 2697af31d1 feat(opcua,host): #81 ServiceLevel SDK publisher
SdkServiceLevelPublisher writes Server.ServiceLevel through the SDK's
ServerObjectState — the standard OPC UA non-transparent-redundancy signal
clients use to pick a primary. Writes are guarded by DiagnosticsLock so
concurrent SDK diagnostics scans don't fight with our updates.

DeferredServiceLevelPublisher mirrors the DeferredAddressSpaceSink late-
binding pattern: Akka actors resolve IServiceLevelPublisher at construction,
hosted service swaps the SDK publisher in after StandardServer.Start. Host
Program.cs registers DeferredServiceLevelPublisher as the singleton bound
to IServiceLevelPublisher; OtOpcUaServerHostedService gets it injected and
fills it once IServerInternal is available.

Tests boot a real StandardServer on a free port (cross-platform), call
Publish, then verify ServerObject.ServiceLevel.Value reflects the write.
5 new tests; OpcUaServer suite now 45/45 green (was 40, +5).

Closes #81 residual. Unblocks Task 60 (OPC UA dual-endpoint + ServiceLevel
tests).
2026-05-26 10:37:42 -04:00

49 lines
1.3 KiB
C#

using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests;
public sealed class DeferredServiceLevelPublisherTests
{
[Fact]
public void Publish_before_SetInner_is_a_safe_noop()
{
var deferred = new DeferredServiceLevelPublisher();
Should.NotThrow(() => deferred.Publish(123));
}
[Fact]
public void Publish_after_SetInner_routes_to_the_inner()
{
var recording = new RecordingPublisher();
var deferred = new DeferredServiceLevelPublisher();
deferred.SetInner(recording);
deferred.Publish(200);
recording.LastValue.ShouldBe((byte)200);
}
[Fact]
public void SetInner_null_reverts_to_Null_publisher()
{
var recording = new RecordingPublisher();
var deferred = new DeferredServiceLevelPublisher();
deferred.SetInner(recording);
deferred.Publish(50);
deferred.SetInner(null);
deferred.Publish(99);
recording.LastValue.ShouldBe((byte)50, "writes after SetInner(null) must not reach the previous inner");
}
private sealed class RecordingPublisher : IServiceLevelPublisher
{
public byte? LastValue { get; private set; }
public void Publish(byte serviceLevel) => LastValue = serviceLevel;
}
}