da4634d67e
v2-ci / build (push) Failing after 44s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Resolves the 12 reported build errors (7 CS0535 sink fakes + 5 CLI CS1587). Runtime.Tests green (74). NOTE: OpcUaServer.Tests still has pre-existing CS7036 errors from the in-progress Galaxy-tag workstream (Phase7Plan/Phase7CompositionResult new required params) — separate, test-only, not addressed here.
93 lines
3.6 KiB
C#
93 lines
3.6 KiB
C#
using System.Collections.Concurrent;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Commons.OpcUa;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests;
|
|
|
|
public sealed class DeferredAddressSpaceSinkTests
|
|
{
|
|
/// <summary>Verifies that the default inner is a null sink so calls before SetSink are safe.</summary>
|
|
[Fact]
|
|
public void Default_inner_is_null_sink_so_calls_before_SetSink_are_safe()
|
|
{
|
|
var deferred = new DeferredAddressSpaceSink();
|
|
|
|
// No throw, no observable side effect.
|
|
deferred.WriteValue("x", 1, OpcUaQuality.Good, DateTime.UtcNow);
|
|
deferred.WriteAlarmState("a", true, false, DateTime.UtcNow);
|
|
deferred.RebuildAddressSpace();
|
|
}
|
|
|
|
/// <summary>Verifies that calls after SetSink are forwarded to the inner sink.</summary>
|
|
[Fact]
|
|
public void Calls_after_SetSink_are_forwarded_to_the_inner()
|
|
{
|
|
var deferred = new DeferredAddressSpaceSink();
|
|
var inner = new RecordingSink();
|
|
deferred.SetSink(inner);
|
|
|
|
deferred.WriteValue("x", 42, OpcUaQuality.Good, DateTime.UtcNow);
|
|
deferred.WriteAlarmState("a-1", true, false, DateTime.UtcNow);
|
|
deferred.RebuildAddressSpace();
|
|
|
|
inner.Calls.ShouldBe(new[] { "WV:x", "WA:a-1", "RB" });
|
|
}
|
|
|
|
/// <summary>Verifies that setting sink to null reverts to null sink.</summary>
|
|
[Fact]
|
|
public void SetSink_to_null_reverts_to_null_sink()
|
|
{
|
|
var deferred = new DeferredAddressSpaceSink();
|
|
var inner = new RecordingSink();
|
|
deferred.SetSink(inner);
|
|
deferred.WriteValue("x", 1, OpcUaQuality.Good, DateTime.UtcNow);
|
|
inner.Calls.Count.ShouldBe(1);
|
|
|
|
deferred.SetSink(null);
|
|
deferred.WriteValue("y", 2, OpcUaQuality.Good, DateTime.UtcNow); // dropped
|
|
inner.Calls.Count.ShouldBe(1);
|
|
}
|
|
|
|
/// <summary>Verifies that sink can be swapped between implementations.</summary>
|
|
[Fact]
|
|
public void SetSink_can_swap_between_implementations()
|
|
{
|
|
var deferred = new DeferredAddressSpaceSink();
|
|
var first = new RecordingSink();
|
|
var second = new RecordingSink();
|
|
|
|
deferred.SetSink(first);
|
|
deferred.WriteValue("a", 1, OpcUaQuality.Good, DateTime.UtcNow);
|
|
|
|
deferred.SetSink(second);
|
|
deferred.WriteValue("b", 2, OpcUaQuality.Good, DateTime.UtcNow);
|
|
|
|
first.Calls.Single().ShouldBe("WV:a");
|
|
second.Calls.Single().ShouldBe("WV:b");
|
|
}
|
|
|
|
private sealed class RecordingSink : IOpcUaAddressSpaceSink
|
|
{
|
|
/// <summary>Gets the queue of recorded calls.</summary>
|
|
public ConcurrentQueue<string> CallQueue { get; } = new();
|
|
/// <summary>Gets the list of recorded calls.</summary>
|
|
public List<string> Calls => CallQueue.ToList();
|
|
|
|
/// <inheritdoc />
|
|
public void WriteValue(string nodeId, object? value, OpcUaQuality quality, DateTime sourceTimestampUtc)
|
|
=> CallQueue.Enqueue($"WV:{nodeId}");
|
|
/// <inheritdoc />
|
|
public void WriteAlarmState(string alarmNodeId, bool active, bool acknowledged, DateTime sourceTimestampUtc)
|
|
=> CallQueue.Enqueue($"WA:{alarmNodeId}");
|
|
/// <inheritdoc />
|
|
public void EnsureFolder(string folderNodeId, string? parentNodeId, string displayName)
|
|
=> CallQueue.Enqueue($"EF:{folderNodeId}");
|
|
/// <inheritdoc />
|
|
public void EnsureVariable(string variableNodeId, string? parentFolderNodeId, string displayName, string dataType)
|
|
=> CallQueue.Enqueue($"EV:{variableNodeId}");
|
|
/// <inheritdoc />
|
|
public void RebuildAddressSpace() => CallQueue.Enqueue("RB");
|
|
}
|
|
}
|