feat(config): remove the SystemPlatform NamespaceKind (capstone) — Galaxy is Equipment-kind

This commit is contained in:
Joseph Doherty
2026-06-12 22:18:56 -04:00
parent 0945f19a50
commit dcbaf63ab1
8 changed files with 60 additions and 73 deletions
@@ -29,14 +29,14 @@ public sealed class UnsTreeServiceTagTests
/// <summary>
/// Seeds an area→line→equipment path in <paramref name="equipmentCluster"/>. The equipment id is
/// always <c>EQ-1</c>. Optionally seeds an Equipment-kind driver (<c>DRV-EQ</c>) in the equipment's
/// cluster, a SystemPlatform-kind driver (<c>DRV-SP</c>) in the equipment's cluster, and an
/// Equipment-kind driver (<c>DRV-OTHER</c>) in <paramref name="otherCluster"/>.
/// cluster, a non-Equipment-kind (Simulated) driver (<c>DRV-SP</c>) in the equipment's cluster, and
/// an Equipment-kind driver (<c>DRV-OTHER</c>) in <paramref name="otherCluster"/>.
/// </summary>
private static void SeedHierarchyAndDrivers(
string dbName,
string equipmentCluster,
bool seedEquipmentDriver = false,
bool seedSystemPlatformDriver = false,
bool seedNonEquipmentDriver = false,
string? otherCluster = null)
{
using var db = UnsTreeTestDb.CreateNamed(dbName);
@@ -59,12 +59,12 @@ public sealed class UnsTreeServiceTagTests
Kind = NamespaceKind.Equipment,
NamespaceUri = "urn:zb:eq",
});
// SystemPlatform-kind namespace in the equipment's cluster.
// Non-Equipment-kind (Simulated) namespace in the equipment's cluster.
db.Namespaces.Add(new Namespace
{
NamespaceId = "NS-SP",
ClusterId = equipmentCluster,
Kind = NamespaceKind.SystemPlatform,
Kind = NamespaceKind.Simulated,
NamespaceUri = "urn:zb:sp",
});
@@ -81,15 +81,15 @@ public sealed class UnsTreeServiceTagTests
});
}
if (seedSystemPlatformDriver)
if (seedNonEquipmentDriver)
{
db.DriverInstances.Add(new DriverInstance
{
DriverInstanceId = "DRV-SP",
ClusterId = equipmentCluster,
NamespaceId = "NS-SP",
Name = "galaxy driver",
DriverType = "Galaxy",
Name = "non-equipment driver",
DriverType = "ModbusTcp",
DriverConfig = "{}",
});
}
@@ -187,12 +187,12 @@ public sealed class UnsTreeServiceTagTests
db.Tags.Any(t => t.TagId == "TAG-1").ShouldBeFalse();
}
/// <summary>Binding a tree tag to a driver in a SystemPlatform-kind namespace is blocked.</summary>
/// <summary>Binding a tree tag to a driver in a non-Equipment-kind namespace is blocked.</summary>
[Fact]
public async Task CreateTag_driver_systemplatform_namespace_blocked()
public async Task CreateTag_driver_non_equipment_namespace_blocked()
{
var (service, dbName) = Fresh();
SeedHierarchyAndDrivers(dbName, equipmentCluster: "MAIN", seedSystemPlatformDriver: true);
SeedHierarchyAndDrivers(dbName, equipmentCluster: "MAIN", seedNonEquipmentDriver: true);
var result = await service.CreateTagAsync("EQ-1", Input("TAG-1", "speed", "DRV-SP"));
@@ -340,7 +340,7 @@ public sealed class UnsTreeServiceTagTests
/// <summary>
/// The driver loader returns only Equipment-kind drivers in the equipment's cluster — excluding
/// SystemPlatform-kind drivers in the same cluster and Equipment-kind drivers in other clusters.
/// non-Equipment-kind drivers in the same cluster and Equipment-kind drivers in other clusters.
/// </summary>
[Fact]
public async Task LoadTagDriversForEquipment_returns_only_equipment_kind_drivers_in_cluster()
@@ -350,7 +350,7 @@ public sealed class UnsTreeServiceTagTests
dbName,
equipmentCluster: "MAIN",
seedEquipmentDriver: true,
seedSystemPlatformDriver: true,
seedNonEquipmentDriver: true,
otherCluster: "SITE-A");
var drivers = await service.LoadTagDriversForEquipmentAsync("EQ-1");
@@ -15,8 +15,8 @@ namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Drivers;
/// <c>EquipmentId</c>) bound to a <c>GalaxyMxGateway</c> driver in an <c>Equipment</c>-kind namespace —
/// into the decoded <c>EquipmentTags</c> with byte-parity to the live-edit composer path: same FullName,
/// EquipmentId, DriverInstanceId, Name, DataType. Both data-contract sites gate purely on the namespace
/// Kind being <c>Equipment</c> (no Galaxy/DriverType exception — the SystemPlatform-mirror contract is
/// retired), so they agree on which tags qualify.
/// Kind being <c>Equipment</c> (no Galaxy/DriverType exception — Galaxy is an ordinary Equipment-kind
/// driver), so they agree on which tags qualify.
/// </summary>
public sealed class DeploymentArtifactAliasParityTests
{
@@ -64,34 +64,34 @@ public sealed class DeploymentArtifactAliasParityTests
tag.FullName.ShouldBe("TestMachine_020.TestChangingInt");
}
/// <summary>An equipment-scoped GalaxyMxGateway tag in a SystemPlatform-kind namespace must NOT surface
/// in EquipmentTags — byte-parity with the composer's pure <c>ns.Kind == NamespaceKind.Equipment</c>
/// predicate. The retired SystemPlatform-mirror contract no longer carried a DriverType exception, so a
/// <summary>An equipment-scoped tag in a non-Equipment (Simulated) namespace must NOT surface in
/// EquipmentTags — byte-parity with the composer's pure <c>ns.Kind == NamespaceKind.Equipment</c>
/// predicate. The gate keys off the namespace Kind alone, with no DriverType exception, so a
/// non-Equipment namespace excludes the tag regardless of driver type.</summary>
[Fact]
public void ParseComposition_excludes_galaxy_tag_in_non_equipment_namespace()
public void ParseComposition_excludes_tag_in_non_equipment_namespace()
{
var blob = JsonSerializer.SerializeToUtf8Bytes(new
{
Namespaces = new[]
{
new { NamespaceId = "ns-sp", Kind = 1 }, // NamespaceKind.SystemPlatform
new { NamespaceId = "ns-sim", Kind = 1 }, // NamespaceKind.Simulated (non-Equipment)
},
DriverInstances = new[]
{
new { DriverInstanceId = "drv-galaxy", DriverType = "GalaxyMxGateway", DriverConfig = "{}", NamespaceId = "ns-sp" },
new { DriverInstanceId = "drv-sim", DriverType = "Modbus", DriverConfig = "{}", NamespaceId = "ns-sim" },
},
Tags = new object[]
{
new
{
TagId = "tag-x",
DriverInstanceId = "drv-galaxy",
DriverInstanceId = "drv-sim",
EquipmentId = "eq-1",
Name = "Source",
FolderPath = (string?)null,
DataType = "Int32",
TagConfig = "{\"FullName\":\"TestMachine_020.Source\"}",
TagConfig = "{\"FullName\":\"40001\"}",
},
},
});
@@ -193,10 +193,10 @@ public sealed class DeploymentArtifactTests
/// <summary>
/// Verifies ParseComposition surfaces Equipment-namespace tags (non-null EquipmentId in an
/// <c>Equipment</c>-kind namespace) as <c>EquipmentTags</c>, with <c>FullName</c> extracted
/// from the tag's TagConfig blob. A tag in a non-Equipment (SystemPlatform) namespace with a
/// from the tag's TagConfig blob. A tag in a non-Equipment (Simulated) namespace with a
/// null EquipmentId must NOT surface in EquipmentTags — byte-parity with the composer's pure
/// <c>ns.Kind == NamespaceKind.Equipment</c> predicate (the SystemPlatform-mirror contract is
/// retired, so such a tag routes nowhere).
/// <c>ns.Kind == NamespaceKind.Equipment</c> predicate (only Equipment-kind namespaces route
/// into EquipmentTags, so such a tag routes nowhere).
/// </summary>
[Fact]
public void ParseComposition_reads_EquipmentTags_from_equipment_namespace()
@@ -206,12 +206,12 @@ public sealed class DeploymentArtifactTests
Namespaces = new[]
{
new { NamespaceId = "ns-eq", Kind = 0 }, // NamespaceKind.Equipment
new { NamespaceId = "ns-sp", Kind = 1 }, // NamespaceKind.SystemPlatform
new { NamespaceId = "ns-sim", Kind = 1 }, // NamespaceKind.Simulated (non-Equipment)
},
DriverInstances = new[]
{
new { DriverInstanceId = "drv-modbus", DriverType = "Modbus", DriverConfig = "{}", NamespaceId = "ns-eq" },
new { DriverInstanceId = "drv-galaxy", DriverType = "Galaxy", DriverConfig = "{}", NamespaceId = "ns-sp" },
new { DriverInstanceId = "drv-sim", DriverType = "Modbus", DriverConfig = "{}", NamespaceId = "ns-sim" },
},
Tags = new object[]
{
@@ -227,8 +227,8 @@ public sealed class DeploymentArtifactTests
},
new
{
TagId = "tag-gx",
DriverInstanceId = "drv-galaxy",
TagId = "tag-sim",
DriverInstanceId = "drv-sim",
EquipmentId = (string?)null,
Name = "Temp",
FolderPath = "area",
@@ -248,9 +248,9 @@ public sealed class DeploymentArtifactTests
tag.DataType.ShouldBe("Float");
tag.FullName.ShouldBe("40001"); // extracted from TagConfig, not the raw blob
// The SystemPlatform tag (null EquipmentId, non-Equipment namespace) does NOT leak into
// EquipmentTags — byte-parity with the composer's pure ns.Kind == Equipment predicate.
c.EquipmentTags.ShouldNotContain(t => t.TagId == "tag-gx");
// The tag in the non-Equipment (Simulated) namespace, with null EquipmentId, does NOT leak
// into EquipmentTags — byte-parity with the composer's pure ns.Kind == Equipment predicate.
c.EquipmentTags.ShouldNotContain(t => t.TagId == "tag-sim");
}
/// <summary>