diff --git a/Libraries/Opc.Ua.Server/Diagnostics/DiagnosticsNodeManager.cs b/Libraries/Opc.Ua.Server/Diagnostics/DiagnosticsNodeManager.cs
index 0ed69b096..10fbb1f8e 100644
--- a/Libraries/Opc.Ua.Server/Diagnostics/DiagnosticsNodeManager.cs
+++ b/Libraries/Opc.Ua.Server/Diagnostics/DiagnosticsNodeManager.cs
@@ -155,8 +155,8 @@ public override void CreateAddressSpace(
// The nodes are now loaded by the DiagnosticsNodeManager from the file
// output by the ModelDesigner V2. These nodes are added to the CoreNodeManager
- // via the AttachNode() method when the DiagnosticsNodeManager starts.
- Server.CoreNodeManager.ImportNodes(SystemContext, PredefinedNodes.Values, true);
+ // via ImportNodes() method when the DiagnosticsNodeManager starts.
+ Server.CoreNodeManager.ImportNodes(SystemContext, PredefinedNodes.Values);
// hook up the server GetMonitoredItems method.
var getMonitoredItems = (GetMonitoredItemsMethodState)FindPredefinedNode(
diff --git a/Libraries/Opc.Ua.Server/NodeManager/CoreNodeManager.cs b/Libraries/Opc.Ua.Server/NodeManager/CoreNodeManager.cs
index 0679ffda4..f0605499a 100644
--- a/Libraries/Opc.Ua.Server/NodeManager/CoreNodeManager.cs
+++ b/Libraries/Opc.Ua.Server/NodeManager/CoreNodeManager.cs
@@ -42,8 +42,10 @@ namespace Opc.Ua.Server
///
/// Every Server has one instance of this NodeManager.
/// It stores objects that implement ILocalNode and indexes them by NodeId.
+ /// This class is deprecated. Use instead.
///
- public class CoreNodeManager : INodeManager, IDisposable
+ [Obsolete("Use CoreNodeManager2 instead. This class will be removed in a future version.")]
+ public class CoreNodeManager : INodeManager, ICoreNodeManager, IDisposable
{
///
/// Initializes the object with default values.
@@ -148,11 +150,11 @@ internal void ImportNodes(
node.Export(context, nodesToExport);
}
- lock (Server.CoreNodeManager.DataLock)
+ lock (DataLock)
{
foreach (ILocalNode nodeToExport in nodesToExport.OfType())
{
- Server.CoreNodeManager.AttachNode(nodeToExport, isInternal);
+ AttachNode(nodeToExport, isInternal);
}
}
}
@@ -3571,6 +3573,11 @@ public NodeId CreateUniqueNodeId()
return CreateUniqueNodeId(m_dynamicNamespaceIndex);
}
+ ///
+ /// Returns the namespace index used for dynamically created nodes.
+ ///
+ public ushort DynamicNamespaceIndex => m_dynamicNamespaceIndex;
+
///
private object GetManagerHandle(ExpandedNodeId nodeId)
{
@@ -3682,6 +3689,37 @@ private NodeId CreateUniqueNodeId(ushort namespaceIndex)
return new NodeId(Utils.IncrementIdentifier(ref m_lastId), namespaceIndex);
}
+ ///
+ /// Called when the session is closed.
+ ///
+ public void SessionClosing(OperationContext context, NodeId sessionId, bool deleteSubscriptions)
+ {
+ // No special handling needed for session closing in the core node manager
+ }
+
+ ///
+ /// Returns true if the node is in the view.
+ ///
+ public bool IsNodeInView(OperationContext context, NodeId viewId, object nodeHandle)
+ {
+ // Core node manager supports all views
+ return true;
+ }
+
+ ///
+ /// Returns the metadata needed for validating permissions, associated with the node.
+ ///
+ public NodeMetadata GetPermissionMetadata(
+ OperationContext context,
+ object targetHandle,
+ BrowseResultMask resultMask,
+ Dictionary> uniqueNodesServiceAttributesCache,
+ bool permissionsOnly)
+ {
+ // Delegate to the standard GetNodeMetadata method
+ return GetNodeMetadata(context, targetHandle, resultMask);
+ }
+
private readonly NodeTable m_nodes;
private uint m_lastId;
private readonly SamplingGroupManager m_samplingGroupManager;
diff --git a/Libraries/Opc.Ua.Server/NodeManager/CoreNodeManager2.cs b/Libraries/Opc.Ua.Server/NodeManager/CoreNodeManager2.cs
new file mode 100644
index 000000000..351843201
--- /dev/null
+++ b/Libraries/Opc.Ua.Server/NodeManager/CoreNodeManager2.cs
@@ -0,0 +1,181 @@
+/* ========================================================================
+ * Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved.
+ *
+ * OPC Foundation MIT License 1.00
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The complete license agreement can be found here:
+ * http://opcfoundation.org/License/MIT/1.00/
+ * ======================================================================*/
+
+using System;
+using System.Collections.Generic;
+using Microsoft.Extensions.Logging;
+
+namespace Opc.Ua.Server
+{
+ ///
+ /// The core node manager for the server based on CustomNodeManager2.
+ ///
+ ///
+ /// Every Server has one instance of this NodeManager.
+ /// It manages the built-in OPC UA nodes and provides core functionality.
+ /// This is a refactored version of CoreNodeManager that inherits from CustomNodeManager2
+ /// to consolidate the NodeManager implementations in the server library.
+ ///
+ public class CoreNodeManager2 : CustomNodeManager2, ICoreNodeManager
+ {
+ ///
+ /// Initializes the node manager with default values.
+ ///
+ public CoreNodeManager2(
+ IServerInternal server,
+ ApplicationConfiguration configuration,
+ ushort dynamicNamespaceIndex)
+ : base(
+ server,
+ configuration,
+ true, // Enable SamplingGroups
+ server.Telemetry.CreateLogger(),
+ Array.Empty()) // CoreNodeManager manages namespaces 0 and 1 by default
+ {
+ if (configuration == null)
+ {
+ throw new ArgumentNullException(nameof(configuration));
+ }
+
+ // Store the dynamic namespace index (typically namespace index 1)
+ m_dynamicNamespaceIndex = dynamicNamespaceIndex;
+
+ // Use namespace 1 if out of range
+ if (m_dynamicNamespaceIndex == 0 ||
+ m_dynamicNamespaceIndex >= server.NamespaceUris.Count)
+ {
+ m_dynamicNamespaceIndex = 1;
+ }
+
+ // Set up namespaces - CoreNodeManager handles namespace 0 (UA) and 1 (server namespace)
+ SetNamespaceIndexes(new ushort[] { 0, m_dynamicNamespaceIndex });
+ }
+
+ ///
+ /// Acquires the lock on the node manager.
+ ///
+ ///
+ /// This property provides compatibility with the old CoreNodeManager API.
+ /// It maps to the Lock property from CustomNodeManager2.
+ ///
+ public object DataLock => Lock;
+
+ ///
+ /// Returns an opaque handle identifying the node to the node manager.
+ ///
+ public override object GetManagerHandle(NodeId nodeId)
+ {
+ lock (Lock)
+ {
+ if (NodeId.IsNull(nodeId))
+ {
+ return null;
+ }
+
+ // Check if it's in namespace 0 (UA standard namespace) or the dynamic namespace
+ if (nodeId.NamespaceIndex != 0 && nodeId.NamespaceIndex != m_dynamicNamespaceIndex)
+ {
+ return null;
+ }
+
+ // Try to find the node in predefined nodes
+ NodeState node = Find(nodeId);
+ if (node != null)
+ {
+ return new NodeHandle(nodeId, node);
+ }
+
+ // Return null if not found (will be handled by other node managers)
+ return null;
+ }
+ }
+
+ ///
+ /// Creates a unique node identifier.
+ ///
+ public NodeId CreateUniqueNodeId()
+ {
+ return CreateUniqueNodeId(m_dynamicNamespaceIndex);
+ }
+
+ ///
+ /// Creates a new unique identifier for a node in the specified namespace.
+ ///
+ private NodeId CreateUniqueNodeId(ushort namespaceIndex)
+ {
+ return new NodeId(Utils.IncrementIdentifier(ref m_lastId), namespaceIndex);
+ }
+
+ ///
+ /// Imports the nodes from a dictionary of NodeState objects.
+ ///
+ public void ImportNodes(ISystemContext context, IEnumerable predefinedNodes)
+ {
+ ImportNodes(context, predefinedNodes, false);
+ }
+
+ ///
+ /// Imports the nodes from a dictionary of NodeState objects.
+ ///
+ internal void ImportNodes(
+ ISystemContext context,
+ IEnumerable predefinedNodes,
+ bool isInternal)
+ {
+ lock (Lock)
+ {
+ foreach (NodeState node in predefinedNodes)
+ {
+ // Add the node to the predefined nodes dictionary
+ AddPredefinedNode(context, node);
+ }
+ }
+ }
+
+ ///
+ /// Attaches a node to the address space.
+ ///
+ ///
+ /// This method is provided for compatibility with the old CoreNodeManager.
+ /// It maps to AddPredefinedNode from CustomNodeManager2.
+ ///
+ internal void AttachNode(NodeState node, bool isInternal)
+ {
+ AddPredefinedNode(SystemContext, node);
+ }
+
+ ///
+ /// Returns the namespace index used for dynamically created nodes.
+ ///
+ public ushort DynamicNamespaceIndex => m_dynamicNamespaceIndex;
+
+ private uint m_lastId;
+ private readonly ushort m_dynamicNamespaceIndex;
+ }
+}
diff --git a/Libraries/Opc.Ua.Server/NodeManager/ICoreNodeManager.cs b/Libraries/Opc.Ua.Server/NodeManager/ICoreNodeManager.cs
new file mode 100644
index 000000000..20fc54515
--- /dev/null
+++ b/Libraries/Opc.Ua.Server/NodeManager/ICoreNodeManager.cs
@@ -0,0 +1,79 @@
+/* ========================================================================
+ * Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved.
+ *
+ * OPC Foundation MIT License 1.00
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The complete license agreement can be found here:
+ * http://opcfoundation.org/License/MIT/1.00/
+ * ======================================================================*/
+
+using System.Collections.Generic;
+
+namespace Opc.Ua.Server
+{
+ ///
+ /// The interface for the core node manager.
+ ///
+ ///
+ /// This interface defines the contract for the core node manager that handles
+ /// the built-in OPC UA nodes (namespace 0) and the server's dynamic namespace.
+ /// It extends INodeManager2 with additional methods specific to the core node manager.
+ ///
+ public interface ICoreNodeManager : INodeManager2
+ {
+ ///
+ /// Acquires the lock on the node manager.
+ ///
+ ///
+ /// This lock should be used when accessing or modifying the node manager's internal state.
+ ///
+ object DataLock { get; }
+
+ ///
+ /// Imports the nodes from a collection of NodeState objects.
+ ///
+ /// The system context.
+ /// The predefined nodes to import.
+ ///
+ /// This method is used to add nodes to the core node manager's address space.
+ /// It is typically called during initialization or when loading predefined nodes.
+ ///
+ void ImportNodes(ISystemContext context, IEnumerable predefinedNodes);
+
+ ///
+ /// Creates a unique node identifier.
+ ///
+ /// A new unique NodeId in the dynamic namespace.
+ ///
+ /// This method generates unique node identifiers for dynamically created nodes.
+ /// The NodeIds are created in the server's dynamic namespace.
+ ///
+ NodeId CreateUniqueNodeId();
+
+ ///
+ /// Returns the namespace index used for dynamically created nodes.
+ ///
+ /// The dynamic namespace index.
+ ushort DynamicNamespaceIndex { get; }
+ }
+}
diff --git a/Libraries/Opc.Ua.Server/NodeManager/MasterNodeManager.cs b/Libraries/Opc.Ua.Server/NodeManager/MasterNodeManager.cs
index 38ebb8fee..558443a10 100644
--- a/Libraries/Opc.Ua.Server/NodeManager/MasterNodeManager.cs
+++ b/Libraries/Opc.Ua.Server/NodeManager/MasterNodeManager.cs
@@ -121,7 +121,7 @@ public MasterNodeManager(
// add the core node manager second because the diagnostics node manager takes priority.
// always add the core node manager to the second of the list.
- var coreNodeManager = new CoreNodeManager(Server, configuration, (ushort)dynamicNamespaceIndex);
+ var coreNodeManager = new CoreNodeManager2(Server, configuration, (ushort)dynamicNamespaceIndex);
m_nodeManagers.Add(coreNodeManager.ToAsyncNodeManager());
// register core node manager for default UA namespace.
@@ -306,7 +306,7 @@ protected static PermissionType GetHistoryPermissionType(PerformUpdateType updat
///
/// Returns the core node manager.
///
- public CoreNodeManager CoreNodeManager => m_nodeManagers[1].SyncNodeManager as CoreNodeManager;
+ public ICoreNodeManager CoreNodeManager => m_nodeManagers[1].SyncNodeManager as ICoreNodeManager;
///
/// Returns the diagnostics node manager.
diff --git a/Libraries/Opc.Ua.Server/Server/IServerInternal.cs b/Libraries/Opc.Ua.Server/Server/IServerInternal.cs
index dee2635d4..08e7171e4 100644
--- a/Libraries/Opc.Ua.Server/Server/IServerInternal.cs
+++ b/Libraries/Opc.Ua.Server/Server/IServerInternal.cs
@@ -96,7 +96,7 @@ public interface IServerInternal : IAuditEventServer, IDisposable
/// The internal node manager for the servers.
///
/// The core node manager.
- CoreNodeManager CoreNodeManager { get; }
+ ICoreNodeManager CoreNodeManager { get; }
///
/// Returns the node manager that managers the server diagnostics.
diff --git a/Libraries/Opc.Ua.Server/Server/ServerInternalData.cs b/Libraries/Opc.Ua.Server/Server/ServerInternalData.cs
index 6032cb8b0..18f514827 100644
--- a/Libraries/Opc.Ua.Server/Server/ServerInternalData.cs
+++ b/Libraries/Opc.Ua.Server/Server/ServerInternalData.cs
@@ -281,7 +281,7 @@ public void SetModellingRulesManager(ModellingRulesManager modellingRulesManager
/// The internal node manager for the servers.
///
/// The core node manager.
- public CoreNodeManager CoreNodeManager { get; private set; }
+ public ICoreNodeManager CoreNodeManager { get; private set; }
///
/// Returns the node manager that managers the server diagnostics.
diff --git a/Tests/Opc.Ua.Server.Tests/CoreNodeManager2Tests.cs b/Tests/Opc.Ua.Server.Tests/CoreNodeManager2Tests.cs
new file mode 100644
index 000000000..a90ad2fe0
--- /dev/null
+++ b/Tests/Opc.Ua.Server.Tests/CoreNodeManager2Tests.cs
@@ -0,0 +1,233 @@
+/* ========================================================================
+ * Copyright (c) 2005-2025 The OPC Foundation, Inc. All rights reserved.
+ *
+ * OPC Foundation MIT License 1.00
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use,
+ * copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following
+ * conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+ * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+ * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The complete license agreement can be found here:
+ * http://opcfoundation.org/License/MIT/1.00/
+ * ======================================================================*/
+
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using NUnit.Framework;
+
+namespace Opc.Ua.Server.Tests
+{
+ ///
+ /// Test
+ ///
+ [TestFixture]
+ [Category("CoreNodeManager2")]
+ [SetCulture("en-us")]
+ [SetUICulture("en-us")]
+ [Parallelizable]
+ public class CoreNodeManager2Tests
+ {
+ ///
+ /// Tests that CoreNodeManager2 is properly instantiated and accessible.
+ ///
+ [Test]
+ public async Task TestCoreNodeManager2Instantiation()
+ {
+ var fixture = new ServerFixture();
+
+ try
+ {
+ // Arrange & Act
+ StandardServer server = await fixture.StartAsync().ConfigureAwait(false);
+
+ // Assert
+ Assert.That(server.CurrentInstance.CoreNodeManager, Is.Not.Null);
+ Assert.That(server.CurrentInstance.CoreNodeManager, Is.InstanceOf());
+ }
+ finally
+ {
+ await fixture.StopAsync().ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Tests that CoreNodeManager2 inherits from CustomNodeManager2.
+ ///
+ [Test]
+ public async Task TestCoreNodeManager2InheritsFromCustomNodeManager2()
+ {
+ var fixture = new ServerFixture();
+
+ try
+ {
+ // Arrange & Act
+ StandardServer server = await fixture.StartAsync().ConfigureAwait(false);
+
+ // Assert
+ Assert.That(server.CurrentInstance.CoreNodeManager, Is.InstanceOf());
+ }
+ finally
+ {
+ await fixture.StopAsync().ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Tests that CoreNodeManager2 has DataLock property for compatibility.
+ ///
+ [Test]
+ public async Task TestCoreNodeManager2HasDataLockProperty()
+ {
+ var fixture = new ServerFixture();
+
+ try
+ {
+ // Arrange & Act
+ StandardServer server = await fixture.StartAsync().ConfigureAwait(false);
+ CoreNodeManager2 coreNodeManager = server.CurrentInstance.CoreNodeManager as CoreNodeManager2;
+
+ // Assert
+ Assert.That(coreNodeManager.DataLock, Is.Not.Null);
+ Assert.That(coreNodeManager.DataLock, Is.EqualTo(coreNodeManager.Lock));
+ }
+ finally
+ {
+ await fixture.StopAsync().ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Tests that CoreNodeManager2 can import nodes.
+ ///
+ [Test]
+ public async Task TestCoreNodeManager2ImportNodes()
+ {
+ var fixture = new ServerFixture();
+
+ try
+ {
+ // Arrange
+ StandardServer server = await fixture.StartAsync().ConfigureAwait(false);
+ CoreNodeManager2 coreNodeManager = server.CurrentInstance.CoreNodeManager as CoreNodeManager2;
+
+ var testNode = new DataItemState(null)
+ {
+ NodeId = new NodeId(Guid.NewGuid(), coreNodeManager.DynamicNamespaceIndex),
+ BrowseName = new QualifiedName("TestNode", coreNodeManager.DynamicNamespaceIndex),
+ DisplayName = "Test Node"
+ };
+
+ // Act
+ coreNodeManager.ImportNodes(coreNodeManager.SystemContext, new NodeState[] { testNode });
+
+ // Assert
+ NodeState foundNode = coreNodeManager.Find(testNode.NodeId);
+ Assert.That(foundNode, Is.Not.Null);
+ Assert.That(foundNode.NodeId, Is.EqualTo(testNode.NodeId));
+ Assert.That(foundNode.BrowseName, Is.EqualTo(testNode.BrowseName));
+ }
+ finally
+ {
+ await fixture.StopAsync().ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Tests that CoreNodeManager2 can create unique node IDs.
+ ///
+ [Test]
+ public async Task TestCoreNodeManager2CreateUniqueNodeId()
+ {
+ var fixture = new ServerFixture();
+
+ try
+ {
+ // Arrange
+ StandardServer server = await fixture.StartAsync().ConfigureAwait(false);
+ CoreNodeManager2 coreNodeManager = server.CurrentInstance.CoreNodeManager as CoreNodeManager2;
+
+ // Act
+ NodeId nodeId1 = coreNodeManager.CreateUniqueNodeId();
+ NodeId nodeId2 = coreNodeManager.CreateUniqueNodeId();
+
+ // Assert
+ Assert.That(nodeId1, Is.Not.Null);
+ Assert.That(nodeId2, Is.Not.Null);
+ Assert.That(nodeId1, Is.Not.EqualTo(nodeId2));
+ Assert.That(nodeId1.NamespaceIndex, Is.EqualTo(coreNodeManager.DynamicNamespaceIndex));
+ Assert.That(nodeId2.NamespaceIndex, Is.EqualTo(coreNodeManager.DynamicNamespaceIndex));
+ }
+ finally
+ {
+ await fixture.StopAsync().ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Tests that CoreNodeManager2 manages namespace 0 and 1.
+ ///
+ [Test]
+ public async Task TestCoreNodeManager2ManagesCorrectNamespaces()
+ {
+ var fixture = new ServerFixture();
+
+ try
+ {
+ // Arrange & Act
+ StandardServer server = await fixture.StartAsync().ConfigureAwait(false);
+ CoreNodeManager2 coreNodeManager = server.CurrentInstance.CoreNodeManager as CoreNodeManager2;
+
+ // Assert
+ Assert.That(coreNodeManager.NamespaceIndexes, Is.Not.Null);
+ Assert.That(coreNodeManager.NamespaceIndexes, Does.Contain((ushort)0)); // UA namespace
+ Assert.That(coreNodeManager.NamespaceIndexes.Count, Is.EqualTo(2));
+ }
+ finally
+ {
+ await fixture.StopAsync().ConfigureAwait(false);
+ }
+ }
+
+ ///
+ /// Tests that CoreNodeManager2 uses SamplingGroups.
+ ///
+ [Test]
+ public async Task TestCoreNodeManager2UsesSamplingGroups()
+ {
+ var fixture = new ServerFixture();
+
+ try
+ {
+ // Arrange & Act
+ StandardServer server = await fixture.StartAsync().ConfigureAwait(false);
+ CoreNodeManager2 coreNodeManager = server.CurrentInstance.CoreNodeManager as CoreNodeManager2;
+
+ // Assert that the node manager is properly initialized
+ // The SamplingGroups support is enabled in the constructor
+ Assert.That(coreNodeManager, Is.Not.Null);
+ Assert.That(coreNodeManager.Server, Is.Not.Null);
+ }
+ finally
+ {
+ await fixture.StopAsync().ConfigureAwait(false);
+ }
+ }
+ }
+}