using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Commons.OpcUa; namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests; /// /// v3 Batch 4 (B4-WP1) — the classifier folds the Raw + UNS subtree diff sets into its routing policy: /// raw/uns adds count as adds (PureAdd), removes as removes (PureRemove), and any Changed raw/uns delta /// routes to Rebuild (the safe default — WP3's applier owns surgical refinement). /// public sealed class AddressSpaceChangeClassifierDualNamespaceTests { private static readonly RawContainerNode Folder = new("Plant", null, "Plant", RawNodeKind.Folder); private static RawTagPlan RawTag(string nodeId) => new("t-1", nodeId, "Plant/Modbus1/PLC-A", "drv-1", "Speed", "Float", Writable: false, Alarm: null, ReferencingEquipmentPaths: Array.Empty()); private static UnsReferenceVariable UnsRef(string nodeId) => new("ref-1", "eq-1", nodeId, "Speed", "Plant/Modbus1/PLC-A/Speed", "Float", Writable: false); private static AddressSpacePlan Empty() => new( Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty()); [Fact] public void Added_raw_tag_only_is_PureAdd() { var plan = Empty() with { AddedRawTags = new[] { RawTag("Plant/Modbus1/PLC-A/Speed") } }; AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureAdd); } [Fact] public void Added_raw_container_only_is_PureAdd() { var plan = Empty() with { AddedRawContainers = new[] { Folder } }; AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureAdd); } [Fact] public void Added_uns_reference_only_is_PureAdd() { var plan = Empty() with { AddedUnsReferenceVariables = new[] { UnsRef("filling/line-1/station-1/Speed") } }; AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureAdd); } [Fact] public void Removed_raw_tag_only_is_PureRemove() { var plan = Empty() with { RemovedRawTags = new[] { RawTag("Plant/Modbus1/PLC-A/Speed") } }; AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureRemove); } [Fact] public void Removed_uns_reference_only_is_PureRemove() { var plan = Empty() with { RemovedUnsReferenceVariables = new[] { UnsRef("filling/line-1/station-1/Speed") } }; AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.PureRemove); } [Fact] public void Raw_rename_add_and_remove_is_AddRemoveMix() { var plan = Empty() with { RemovedRawTags = new[] { RawTag("Plant/Modbus1/PLC-A/Speed") }, AddedRawTags = new[] { RawTag("Plant/Modbus1/PLC-A/Velocity") }, }; AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.AddRemoveMix); } [Fact] public void Changed_raw_tag_is_Rebuild() { var plan = Empty() with { ChangedRawTags = new[] { new AddressSpacePlan.RawTagDelta( RawTag("Plant/Modbus1/PLC-A/Speed"), RawTag("Plant/Modbus1/PLC-A/Speed") with { IsHistorized = true }), }, }; AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.Rebuild); } [Fact] public void Uns_reference_repoint_is_Rebuild() { var plan = Empty() with { ChangedUnsReferenceVariables = new[] { new AddressSpacePlan.UnsReferenceDelta( UnsRef("filling/line-1/station-1/Speed"), UnsRef("filling/line-1/station-1/Speed") with { BackingRawPath = "Plant/Modbus1/PLC-A/Velocity" }), }, }; AddressSpaceChangeClassifier.Classify(plan).ShouldBe(AddressSpaceChangeKind.Rebuild); } }