using System.Reflection; using Opc.Ua; using Opc.Ua.Server; using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests; /// /// Gitea #838 review: OtOpcUaNodeManager.DeliverConditionEventAsync re-implements the SDK's event walk /// (NodeState.ReportEventAsync, plus BaseInstanceState's forward to its Parent) so it can re-check the /// condition's liveness before every hop. That copy is only faithful while no other node type changes the walk — /// missing the Parent hop alone silently stopped condition events reaching their folders. This pins the set of /// types that define ReportEvent / ReportEventAsync across the SDK and this server. /// public sealed class ConditionEventWalkGuardTests { [Fact] public void Only_NodeState_and_BaseInstanceState_define_the_event_walk() { Assembly[] assemblies = [ typeof(NodeState).Assembly, // Opc.Ua.Types typeof(ApplicationConfiguration).Assembly, // Opc.Ua.Core typeof(StandardServer).Assembly, // Opc.Ua.Server typeof(OtOpcUaNodeManager).Assembly, // this server ]; var definers = assemblies.Distinct() .SelectMany(a => a.GetTypes()) .Where(t => typeof(NodeState).IsAssignableFrom(t)) .Where(t => t.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly) .Any(m => m.Name is nameof(NodeState.ReportEvent) or nameof(NodeState.ReportEventAsync))) .Select(t => t.FullName) .Order() .ToArray(); definers.ShouldBe(["Opc.Ua.BaseInstanceState", "Opc.Ua.NodeState"], "a new override changes the event walk — mirror it in OtOpcUaNodeManager.DeliverConditionEventAsync"); } }