using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; using ZB.MOM.WW.OtOpcUa.Server.OpcUa; namespace ZB.MOM.WW.OtOpcUa.Server.Tests; /// /// Phase 7 Stream G follow-up — verifies the NodeSourceKind dispatch kernel that /// DriverNodeManager's OnReadValue + OnWriteValue use to route per-node calls to /// the right backend per ADR-002. Pure functions; no OPC UA stack required. /// [Trait("Category", "Unit")] public sealed class DriverNodeManagerSourceDispatchTests { private sealed class FakeReadable : IReadable { public string Name { get; init; } = ""; public Task> ReadAsync( IReadOnlyList fullReferences, CancellationToken cancellationToken) => Task.FromResult>([]); } [Fact] public void Driver_source_routes_to_driver_readable() { var drv = new FakeReadable { Name = "drv" }; var vt = new FakeReadable { Name = "vt" }; var al = new FakeReadable { Name = "al" }; DriverNodeManager.SelectReadable(NodeSourceKind.Driver, drv, vt, al).ShouldBeSameAs(drv); } [Fact] public void Virtual_source_routes_to_virtual_readable() { var drv = new FakeReadable(); var vt = new FakeReadable(); var al = new FakeReadable(); DriverNodeManager.SelectReadable(NodeSourceKind.Virtual, drv, vt, al).ShouldBeSameAs(vt); } [Fact] public void ScriptedAlarm_source_routes_to_alarm_readable() { var drv = new FakeReadable(); var vt = new FakeReadable(); var al = new FakeReadable(); DriverNodeManager.SelectReadable(NodeSourceKind.ScriptedAlarm, drv, vt, al).ShouldBeSameAs(al); } [Fact] public void Virtual_source_without_virtual_readable_returns_null() { // Engine not wired → dispatch layer surfaces BadNotFound (the null propagates // through to the OnReadValue null-check). DriverNodeManager.SelectReadable( NodeSourceKind.Virtual, driverReadable: new FakeReadable(), virtualReadable: null, scriptedAlarmReadable: null).ShouldBeNull(); } [Fact] public void ScriptedAlarm_source_without_alarm_readable_returns_null() { DriverNodeManager.SelectReadable( NodeSourceKind.ScriptedAlarm, driverReadable: new FakeReadable(), virtualReadable: new FakeReadable(), scriptedAlarmReadable: null).ShouldBeNull(); } [Fact] public void Driver_source_without_driver_readable_returns_null() { // Pre-existing BadNotReadable behavior — unchanged by Phase 7 wiring. DriverNodeManager.SelectReadable( NodeSourceKind.Driver, driverReadable: null, virtualReadable: new FakeReadable(), scriptedAlarmReadable: new FakeReadable()).ShouldBeNull(); } [Fact] public void IsWriteAllowedBySource_only_Driver_returns_true() { // Plan decision #6 — OPC UA writes to virtual tags / scripted alarms rejected. DriverNodeManager.IsWriteAllowedBySource(NodeSourceKind.Driver).ShouldBeTrue(); DriverNodeManager.IsWriteAllowedBySource(NodeSourceKind.Virtual).ShouldBeFalse(); DriverNodeManager.IsWriteAllowedBySource(NodeSourceKind.ScriptedAlarm).ShouldBeFalse(); } }