test: fix Galaxy-tag Phase7 test fixtures + S7 CLI enum; add MaterialiseGalaxyTags coverage
v2-ci / build (push) Failing after 38s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
v2-ci / build (push) Failing after 38s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped
Completes the test side of the in-progress Galaxy-tag workstream: - Phase7ApplierTests / Phase7ApplierHierarchyTests: supply the now-required Galaxy-tag args to Phase7Plan / Phase7CompositionResult. - Add genuine coverage for Phase7Applier.MaterialiseGalaxyTags (folder-per-distinct-path, variable-per-tag node-id derivation, folder dedupe) + added-Galaxy-tags-trigger-rebuild. - S7.Cli.Tests: use the project's S7CpuType (CLI option type) instead of S7.Net.CpuType. Whole solution now builds 0/0; OpcUaServer.Tests 52, S7.Cli.Tests 36 green.
This commit is contained in:
+4
-4
@@ -2,8 +2,8 @@ using CliFx.Attributes;
|
||||
using CliFx.Infrastructure;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.S7;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.S7.Cli;
|
||||
using S7NetCpuType = global::S7.Net.CpuType;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.S7.Cli.Tests;
|
||||
|
||||
@@ -41,7 +41,7 @@ public sealed class S7CommandBaseBuildOptionsTests
|
||||
{
|
||||
Host = "10.0.0.5",
|
||||
Port = 102,
|
||||
CpuType = S7NetCpuType.S71500,
|
||||
CpuType = S7CpuType.S71500,
|
||||
Rack = 0,
|
||||
Slot = 0,
|
||||
TimeoutMs = 5000,
|
||||
@@ -72,7 +72,7 @@ public sealed class S7CommandBaseBuildOptionsTests
|
||||
{
|
||||
Host = "plc.shop.local",
|
||||
Port = 4102,
|
||||
CpuType = S7NetCpuType.S7300,
|
||||
CpuType = S7CpuType.S7300,
|
||||
Rack = 1,
|
||||
Slot = 2,
|
||||
TimeoutMs = 3000,
|
||||
@@ -82,7 +82,7 @@ public sealed class S7CommandBaseBuildOptionsTests
|
||||
|
||||
options.Host.ShouldBe("plc.shop.local");
|
||||
options.Port.ShouldBe(4102);
|
||||
options.CpuType.ShouldBe(S7NetCpuType.S7300);
|
||||
options.CpuType.ShouldBe(S7CpuType.S7300);
|
||||
options.Rack.ShouldBe((short)1);
|
||||
options.Slot.ShouldBe((short)2);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,8 @@ public sealed class Phase7ApplierHierarchyTests : IDisposable
|
||||
UnsLines: new[] { new UnsLineProjection("line-1", "area-1", "Cell A") },
|
||||
EquipmentNodes: new[] { new EquipmentNode("eq-1", "Pump-1", "line-1") },
|
||||
DriverInstancePlans: Array.Empty<DriverInstancePlan>(),
|
||||
ScriptedAlarmPlans: Array.Empty<ScriptedAlarmPlan>());
|
||||
ScriptedAlarmPlans: Array.Empty<ScriptedAlarmPlan>(),
|
||||
GalaxyTags: Array.Empty<GalaxyTagPlan>());
|
||||
|
||||
applier.MaterialiseHierarchy(composition);
|
||||
|
||||
@@ -57,7 +58,8 @@ public sealed class Phase7ApplierHierarchyTests : IDisposable
|
||||
UnsLines: Array.Empty<UnsLineProjection>(),
|
||||
EquipmentNodes: new[] { new EquipmentNode("eq-orphan", "Orphan", UnsLineId: "") },
|
||||
DriverInstancePlans: Array.Empty<DriverInstancePlan>(),
|
||||
ScriptedAlarmPlans: Array.Empty<ScriptedAlarmPlan>());
|
||||
ScriptedAlarmPlans: Array.Empty<ScriptedAlarmPlan>(),
|
||||
GalaxyTags: Array.Empty<GalaxyTagPlan>());
|
||||
|
||||
applier.MaterialiseHierarchy(composition);
|
||||
|
||||
@@ -91,7 +93,8 @@ public sealed class Phase7ApplierHierarchyTests : IDisposable
|
||||
UnsLines: new[] { new UnsLineProjection("line-1", "area-A", "Line 1") },
|
||||
EquipmentNodes: new[] { new EquipmentNode("eq-1", "Eq 1", "line-1"), new EquipmentNode("eq-2", "Eq 2", "line-1") },
|
||||
DriverInstancePlans: Array.Empty<DriverInstancePlan>(),
|
||||
ScriptedAlarmPlans: Array.Empty<ScriptedAlarmPlan>()));
|
||||
ScriptedAlarmPlans: Array.Empty<ScriptedAlarmPlan>(),
|
||||
GalaxyTags: Array.Empty<GalaxyTagPlan>()));
|
||||
|
||||
sdkServer.NodeManager!.FolderCount.ShouldBe(5); // 2 areas + 1 line + 2 equipment
|
||||
|
||||
@@ -101,7 +104,8 @@ public sealed class Phase7ApplierHierarchyTests : IDisposable
|
||||
UnsLines: new[] { new UnsLineProjection("line-1", "area-A", "Line 1") },
|
||||
EquipmentNodes: new[] { new EquipmentNode("eq-1", "Eq 1", "line-1"), new EquipmentNode("eq-2", "Eq 2", "line-1") },
|
||||
DriverInstancePlans: Array.Empty<DriverInstancePlan>(),
|
||||
ScriptedAlarmPlans: Array.Empty<ScriptedAlarmPlan>()));
|
||||
ScriptedAlarmPlans: Array.Empty<ScriptedAlarmPlan>(),
|
||||
GalaxyTags: Array.Empty<GalaxyTagPlan>()));
|
||||
|
||||
sdkServer.NodeManager!.FolderCount.ShouldBe(5);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,10 @@ public sealed class Phase7ApplierTests
|
||||
ChangedDrivers: Array.Empty<Phase7Plan.DriverDelta>(),
|
||||
AddedAlarms: Array.Empty<ScriptedAlarmPlan>(),
|
||||
RemovedAlarms: Array.Empty<ScriptedAlarmPlan>(),
|
||||
ChangedAlarms: Array.Empty<Phase7Plan.AlarmDelta>());
|
||||
ChangedAlarms: Array.Empty<Phase7Plan.AlarmDelta>(),
|
||||
AddedGalaxyTags: Array.Empty<GalaxyTagPlan>(),
|
||||
RemovedGalaxyTags: Array.Empty<GalaxyTagPlan>(),
|
||||
ChangedGalaxyTags: Array.Empty<Phase7Plan.GalaxyTagDelta>());
|
||||
|
||||
var outcome = applier.Apply(plan);
|
||||
|
||||
@@ -89,7 +92,10 @@ public sealed class Phase7ApplierTests
|
||||
},
|
||||
AddedAlarms: Array.Empty<ScriptedAlarmPlan>(),
|
||||
RemovedAlarms: Array.Empty<ScriptedAlarmPlan>(),
|
||||
ChangedAlarms: Array.Empty<Phase7Plan.AlarmDelta>());
|
||||
ChangedAlarms: Array.Empty<Phase7Plan.AlarmDelta>(),
|
||||
AddedGalaxyTags: Array.Empty<GalaxyTagPlan>(),
|
||||
RemovedGalaxyTags: Array.Empty<GalaxyTagPlan>(),
|
||||
ChangedGalaxyTags: Array.Empty<Phase7Plan.GalaxyTagDelta>());
|
||||
|
||||
var outcome = applier.Apply(plan);
|
||||
|
||||
@@ -111,10 +117,102 @@ public sealed class Phase7ApplierTests
|
||||
outcome.RebuildCalled.ShouldBeTrue();
|
||||
}
|
||||
|
||||
/// <summary>Verifies MaterialiseGalaxyTags creates one folder per distinct FolderPath and one
|
||||
/// variable per tag, with root-level tags hung directly under the namespace root.</summary>
|
||||
[Fact]
|
||||
public void MaterialiseGalaxyTags_creates_folder_per_distinct_path_and_variable_per_tag()
|
||||
{
|
||||
var sink = new RecordingSink();
|
||||
var applier = new Phase7Applier(sink, NullLogger<Phase7Applier>.Instance);
|
||||
|
||||
var composition = new Phase7CompositionResult(
|
||||
UnsAreas: Array.Empty<UnsAreaProjection>(),
|
||||
UnsLines: Array.Empty<UnsLineProjection>(),
|
||||
EquipmentNodes: Array.Empty<EquipmentNode>(),
|
||||
DriverInstancePlans: Array.Empty<DriverInstancePlan>(),
|
||||
ScriptedAlarmPlans: Array.Empty<ScriptedAlarmPlan>(),
|
||||
GalaxyTags: new[]
|
||||
{
|
||||
new GalaxyTagPlan("t1", "drv", "section.area", "Temperature", "Float", "section.area.Temperature"),
|
||||
new GalaxyTagPlan("t2", "drv", "", "Pressure", "Int32", "Pressure"),
|
||||
});
|
||||
|
||||
applier.MaterialiseGalaxyTags(composition);
|
||||
|
||||
// One folder for the single distinct non-empty FolderPath; the root-level tag adds none.
|
||||
sink.FolderCalls.ShouldHaveSingleItem().ShouldBe(("section.area", null, "section.area"));
|
||||
|
||||
// Foldered tag → NodeId is its MxAccessRef under the FolderPath parent.
|
||||
// Root-level tag → NodeId is its DisplayName under the root (null parent).
|
||||
sink.VariableCalls.ShouldContain(("section.area.Temperature", "section.area", "Temperature", "Float"));
|
||||
sink.VariableCalls.ShouldContain(("Pressure", (string?)null, "Pressure", "Int32"));
|
||||
sink.VariableCalls.Count.ShouldBe(2);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that two tags sharing a FolderPath produce a single EnsureFolder call
|
||||
/// (deduped) but one EnsureVariable per tag.</summary>
|
||||
[Fact]
|
||||
public void MaterialiseGalaxyTags_dedupes_folders_for_tags_sharing_a_path()
|
||||
{
|
||||
var sink = new RecordingSink();
|
||||
var applier = new Phase7Applier(sink, NullLogger<Phase7Applier>.Instance);
|
||||
|
||||
var composition = new Phase7CompositionResult(
|
||||
UnsAreas: Array.Empty<UnsAreaProjection>(),
|
||||
UnsLines: Array.Empty<UnsLineProjection>(),
|
||||
EquipmentNodes: Array.Empty<EquipmentNode>(),
|
||||
DriverInstancePlans: Array.Empty<DriverInstancePlan>(),
|
||||
ScriptedAlarmPlans: Array.Empty<ScriptedAlarmPlan>(),
|
||||
GalaxyTags: new[]
|
||||
{
|
||||
new GalaxyTagPlan("t1", "drv", "line.cell", "Speed", "Float", "line.cell.Speed"),
|
||||
new GalaxyTagPlan("t2", "drv", "line.cell", "Torque", "Float", "line.cell.Torque"),
|
||||
});
|
||||
|
||||
applier.MaterialiseGalaxyTags(composition);
|
||||
|
||||
sink.FolderCalls.ShouldHaveSingleItem().ShouldBe(("line.cell", null, "line.cell"));
|
||||
sink.VariableCalls.Count.ShouldBe(2);
|
||||
sink.VariableCalls.ShouldContain(("line.cell.Speed", "line.cell", "Speed", "Float"));
|
||||
sink.VariableCalls.ShouldContain(("line.cell.Torque", "line.cell", "Torque", "Float"));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that added Galaxy tags in an otherwise-empty plan trigger an address-space rebuild.</summary>
|
||||
[Fact]
|
||||
public void Added_galaxy_tags_trigger_rebuild()
|
||||
{
|
||||
var sink = new RecordingSink();
|
||||
var applier = new Phase7Applier(sink, NullLogger<Phase7Applier>.Instance);
|
||||
|
||||
var plan = new Phase7Plan(
|
||||
AddedEquipment: Array.Empty<EquipmentNode>(),
|
||||
RemovedEquipment: Array.Empty<EquipmentNode>(),
|
||||
ChangedEquipment: Array.Empty<Phase7Plan.EquipmentDelta>(),
|
||||
AddedDrivers: Array.Empty<DriverInstancePlan>(),
|
||||
RemovedDrivers: Array.Empty<DriverInstancePlan>(),
|
||||
ChangedDrivers: Array.Empty<Phase7Plan.DriverDelta>(),
|
||||
AddedAlarms: Array.Empty<ScriptedAlarmPlan>(),
|
||||
RemovedAlarms: Array.Empty<ScriptedAlarmPlan>(),
|
||||
ChangedAlarms: Array.Empty<Phase7Plan.AlarmDelta>(),
|
||||
AddedGalaxyTags: new[]
|
||||
{
|
||||
new GalaxyTagPlan("t1", "drv", "section.area", "Temperature", "Float", "section.area.Temperature"),
|
||||
},
|
||||
RemovedGalaxyTags: Array.Empty<GalaxyTagPlan>(),
|
||||
ChangedGalaxyTags: Array.Empty<Phase7Plan.GalaxyTagDelta>());
|
||||
|
||||
var outcome = applier.Apply(plan);
|
||||
|
||||
outcome.RebuildCalled.ShouldBeTrue();
|
||||
outcome.AddedNodes.ShouldBe(1);
|
||||
sink.RebuildCalls.ShouldBe(1);
|
||||
}
|
||||
|
||||
private static Phase7Plan EmptyPlan => new(
|
||||
Array.Empty<EquipmentNode>(), Array.Empty<EquipmentNode>(), Array.Empty<Phase7Plan.EquipmentDelta>(),
|
||||
Array.Empty<DriverInstancePlan>(), Array.Empty<DriverInstancePlan>(), Array.Empty<Phase7Plan.DriverDelta>(),
|
||||
Array.Empty<ScriptedAlarmPlan>(), Array.Empty<ScriptedAlarmPlan>(), Array.Empty<Phase7Plan.AlarmDelta>());
|
||||
Array.Empty<ScriptedAlarmPlan>(), Array.Empty<ScriptedAlarmPlan>(), Array.Empty<Phase7Plan.AlarmDelta>(),
|
||||
Array.Empty<GalaxyTagPlan>(), Array.Empty<GalaxyTagPlan>(), Array.Empty<Phase7Plan.GalaxyTagDelta>());
|
||||
|
||||
private static Phase7Plan WithEquipmentRemoval(params string[] ids) => new(
|
||||
AddedEquipment: Array.Empty<EquipmentNode>(),
|
||||
@@ -125,7 +223,10 @@ public sealed class Phase7ApplierTests
|
||||
ChangedDrivers: Array.Empty<Phase7Plan.DriverDelta>(),
|
||||
AddedAlarms: Array.Empty<ScriptedAlarmPlan>(),
|
||||
RemovedAlarms: Array.Empty<ScriptedAlarmPlan>(),
|
||||
ChangedAlarms: Array.Empty<Phase7Plan.AlarmDelta>());
|
||||
ChangedAlarms: Array.Empty<Phase7Plan.AlarmDelta>(),
|
||||
AddedGalaxyTags: Array.Empty<GalaxyTagPlan>(),
|
||||
RemovedGalaxyTags: Array.Empty<GalaxyTagPlan>(),
|
||||
ChangedGalaxyTags: Array.Empty<Phase7Plan.GalaxyTagDelta>());
|
||||
|
||||
private sealed class RecordingSink : IOpcUaAddressSpaceSink
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user