using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests; public sealed class Phase7PlannerTests { [Fact] public void Empty_inputs_produce_empty_plan() { var prev = new Phase7CompositionResult(Array.Empty(), Array.Empty(), Array.Empty()); var next = prev; var plan = Phase7Planner.Compute(prev, next); plan.IsEmpty.ShouldBeTrue(); } [Fact] public void Identical_compositions_produce_empty_plan() { var eq = new EquipmentNode("eq-1", "Eq 1", "line-1"); var prev = new Phase7CompositionResult(new[] { eq }, Array.Empty(), Array.Empty()); var next = new Phase7CompositionResult(new[] { eq }, Array.Empty(), Array.Empty()); var plan = Phase7Planner.Compute(prev, next); plan.IsEmpty.ShouldBeTrue(); } [Fact] public void New_equipment_goes_to_AddedEquipment() { var prev = new Phase7CompositionResult(Array.Empty(), Array.Empty(), Array.Empty()); var next = new Phase7CompositionResult( new[] { new EquipmentNode("eq-1", "A", "line-1") }, Array.Empty(), Array.Empty()); var plan = Phase7Planner.Compute(prev, next); plan.AddedEquipment.Single().EquipmentId.ShouldBe("eq-1"); plan.RemovedEquipment.ShouldBeEmpty(); plan.ChangedEquipment.ShouldBeEmpty(); } [Fact] public void Disappeared_equipment_goes_to_RemovedEquipment() { var prev = new Phase7CompositionResult( new[] { new EquipmentNode("eq-1", "A", "line-1") }, Array.Empty(), Array.Empty()); var next = new Phase7CompositionResult(Array.Empty(), Array.Empty(), Array.Empty()); var plan = Phase7Planner.Compute(prev, next); plan.RemovedEquipment.Single().EquipmentId.ShouldBe("eq-1"); plan.AddedEquipment.ShouldBeEmpty(); } [Fact] public void Same_id_with_different_display_name_routes_to_ChangedEquipment() { var prev = new Phase7CompositionResult( new[] { new EquipmentNode("eq-1", "Old", "line-1") }, Array.Empty(), Array.Empty()); var next = new Phase7CompositionResult( new[] { new EquipmentNode("eq-1", "New", "line-1") }, Array.Empty(), Array.Empty()); var plan = Phase7Planner.Compute(prev, next); plan.ChangedEquipment.Single().Previous.DisplayName.ShouldBe("Old"); plan.ChangedEquipment.Single().Current.DisplayName.ShouldBe("New"); plan.AddedEquipment.ShouldBeEmpty(); plan.RemovedEquipment.ShouldBeEmpty(); } [Fact] public void Driver_config_change_routes_to_ChangedDrivers() { var prev = new Phase7CompositionResult( Array.Empty(), new[] { new DriverInstancePlan("drv-1", "Modbus", "{\"host\":\"old\"}") }, Array.Empty()); var next = new Phase7CompositionResult( Array.Empty(), new[] { new DriverInstancePlan("drv-1", "Modbus", "{\"host\":\"new\"}") }, Array.Empty()); var plan = Phase7Planner.Compute(prev, next); plan.ChangedDrivers.Single().Current.ConfigJson.ShouldContain("new"); } [Fact] public void Alarm_message_template_change_routes_to_ChangedAlarms() { var prev = new Phase7CompositionResult( Array.Empty(), Array.Empty(), new[] { new ScriptedAlarmPlan("a-1", "eq-1", "script-1", "old") }); var next = new Phase7CompositionResult( Array.Empty(), Array.Empty(), new[] { new ScriptedAlarmPlan("a-1", "eq-1", "script-1", "new") }); var plan = Phase7Planner.Compute(prev, next); plan.ChangedAlarms.Single().Current.MessageTemplate.ShouldBe("new"); } [Fact] public void Added_and_removed_lists_are_sorted_by_id_for_deterministic_ordering() { var prev = new Phase7CompositionResult( new[] { new EquipmentNode("z", "Z", "line-1"), new EquipmentNode("a", "A", "line-1") }, Array.Empty(), Array.Empty()); var next = new Phase7CompositionResult(Array.Empty(), Array.Empty(), Array.Empty()); var plan = Phase7Planner.Compute(prev, next); plan.RemovedEquipment.Select(e => e.EquipmentId).ShouldBe(new[] { "a", "z" }); } [Fact] public void Mixed_changes_across_all_three_classes_are_captured_in_one_pass() { var prev = new Phase7CompositionResult( new[] { new EquipmentNode("eq-keep", "Keep", "line-1"), new EquipmentNode("eq-drop", "Drop", "line-1") }, new[] { new DriverInstancePlan("drv-keep", "Modbus", "{}"), new DriverInstancePlan("drv-change", "Modbus", "{\"v\":1}") }, new[] { new ScriptedAlarmPlan("a-keep", "eq-keep", "s1", "t1") }); var next = new Phase7CompositionResult( new[] { new EquipmentNode("eq-keep", "Keep", "line-1"), new EquipmentNode("eq-new", "New", "line-1") }, new[] { new DriverInstancePlan("drv-keep", "Modbus", "{}"), new DriverInstancePlan("drv-change", "Modbus", "{\"v\":2}") }, new[] { new ScriptedAlarmPlan("a-keep", "eq-keep", "s1", "t1"), new ScriptedAlarmPlan("a-new", "eq-new", "s2", "t2") }); var plan = Phase7Planner.Compute(prev, next); plan.AddedEquipment.Single().EquipmentId.ShouldBe("eq-new"); plan.RemovedEquipment.Single().EquipmentId.ShouldBe("eq-drop"); plan.ChangedEquipment.ShouldBeEmpty(); plan.ChangedDrivers.Single().Current.DriverInstanceId.ShouldBe("drv-change"); plan.AddedAlarms.Single().ScriptedAlarmId.ShouldBe("a-new"); } }