using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Configuration.Entities; using ZB.MOM.WW.OtOpcUa.Configuration.Enums; using ZB.MOM.WW.OtOpcUa.Core.OpcUa; using ZB.MOM.WW.OtOpcUa.Server.Phase7; namespace ZB.MOM.WW.OtOpcUa.Server.Tests.Phase7; /// /// Task #246 — covers the deterministic mapping inside /// that turns into the path → fullRef map /// consumes. Pure function; no DI / DB needed. /// [Trait("Category", "Unit")] public sealed class Phase7ComposerMappingTests { private static UnsArea Area(string id, string name) => new() { UnsAreaId = id, ClusterId = "c", Name = name, GenerationId = 1 }; private static UnsLine Line(string id, string areaId, string name) => new() { UnsLineId = id, UnsAreaId = areaId, Name = name, GenerationId = 1 }; private static Equipment Eq(string id, string lineId, string name) => new() { EquipmentRowId = Guid.NewGuid(), GenerationId = 1, EquipmentId = id, EquipmentUuid = Guid.NewGuid(), DriverInstanceId = "drv", UnsLineId = lineId, Name = name, MachineCode = "m", }; private static Tag T(string id, string name, string fullRef, string equipmentId) => new() { TagRowId = Guid.NewGuid(), GenerationId = 1, TagId = id, DriverInstanceId = "drv", EquipmentId = equipmentId, Name = name, DataType = "Float32", AccessLevel = TagAccessLevel.Read, TagConfig = fullRef, }; [Fact] public void Maps_tag_to_UNS_path_walker_emits() { var content = new EquipmentNamespaceContent( Areas: [Area("a1", "warsaw")], Lines: [Line("l1", "a1", "oven-line")], Equipment: [Eq("e1", "l1", "oven-3")], Tags: [T("t1", "Temp", "DR.Temp", "e1")]); var map = Phase7Composer.MapPathsToFullRefs(content); map.ShouldContainKeyAndValue("/warsaw/oven-line/oven-3/Temp", "DR.Temp"); } [Fact] public void Skips_tag_with_null_EquipmentId() { var content = new EquipmentNamespaceContent( [Area("a1", "warsaw")], [Line("l1", "a1", "ol")], [Eq("e1", "l1", "ov")], [T("t1", "Bare", "DR.Bare", null!)]); // SystemPlatform-style orphan Phase7Composer.MapPathsToFullRefs(content).ShouldBeEmpty(); } [Fact] public void Skips_tag_pointing_at_unknown_Equipment() { var content = new EquipmentNamespaceContent( [Area("a1", "warsaw")], [Line("l1", "a1", "ol")], [Eq("e1", "l1", "ov")], [T("t1", "Lost", "DR.Lost", "e-missing")]); Phase7Composer.MapPathsToFullRefs(content).ShouldBeEmpty(); } [Fact] public void Maps_multiple_tags_under_same_equipment_distinctly() { var content = new EquipmentNamespaceContent( [Area("a1", "site")], [Line("l1", "a1", "line1")], [Eq("e1", "l1", "cell")], [T("t1", "Temp", "DR.T", "e1"), T("t2", "Pressure", "DR.P", "e1")]); var map = Phase7Composer.MapPathsToFullRefs(content); map.Count.ShouldBe(2); map["/site/line1/cell/Temp"].ShouldBe("DR.T"); map["/site/line1/cell/Pressure"].ShouldBe("DR.P"); } [Fact] public void Empty_content_yields_empty_map() { Phase7Composer.MapPathsToFullRefs(new EquipmentNamespaceContent([], [], [], [])) .ShouldBeEmpty(); } }