using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Core.Authorization; using ZB.MOM.WW.OtOpcUa.Server.Security; namespace ZB.MOM.WW.OtOpcUa.Server.Tests; [Trait("Category", "Unit")] public sealed class NodeScopeResolverTests { [Fact] public void Resolve_PopulatesClusterAndTag() { var resolver = new NodeScopeResolver("c-warsaw"); var scope = resolver.Resolve("TestMachine_001/Oven/SetPoint"); scope.ClusterId.ShouldBe("c-warsaw"); scope.TagId.ShouldBe("TestMachine_001/Oven/SetPoint"); scope.Kind.ShouldBe(NodeHierarchyKind.Equipment); } [Fact] public void Resolve_Leaves_UnsPath_Null_For_Phase1() { var resolver = new NodeScopeResolver("c-1"); var scope = resolver.Resolve("tag-1"); // Phase 1 flat scope — finer resolution tracked as Stream C.12 follow-up. scope.NamespaceId.ShouldBeNull(); scope.UnsAreaId.ShouldBeNull(); scope.UnsLineId.ShouldBeNull(); scope.EquipmentId.ShouldBeNull(); } [Fact] public void Resolve_Throws_OnEmptyFullReference() { var resolver = new NodeScopeResolver("c-1"); Should.Throw(() => resolver.Resolve("")); Should.Throw(() => resolver.Resolve(" ")); } [Fact] public void Ctor_Throws_OnEmptyClusterId() { Should.Throw(() => new NodeScopeResolver("")); } [Fact] public void Resolver_IsStateless_AcrossCalls() { var resolver = new NodeScopeResolver("c"); var s1 = resolver.Resolve("tag-a"); var s2 = resolver.Resolve("tag-b"); s1.TagId.ShouldBe("tag-a"); s2.TagId.ShouldBe("tag-b"); s1.ClusterId.ShouldBe("c"); s2.ClusterId.ShouldBe("c"); } }