feat(config): DraftValidator rule + DraftSnapshot.VirtualTags for Tag/VirtualTag NodeId collisions
This commit is contained in:
@@ -186,6 +186,76 @@ public sealed class DraftValidatorTests
|
||||
errors.ShouldContain(e => e.Code == "UnsSegmentInvalid");
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// ValidateNoEquipmentSignalNameCollision — Tag/VirtualTag NodeId collision
|
||||
// ------------------------------------------------------------------------------------
|
||||
|
||||
/// <summary>Verifies that an equipment-bound Tag and a VirtualTag sharing the same
|
||||
/// equipment+name collide on a single OPC UA NodeId.</summary>
|
||||
[Fact]
|
||||
public void Tag_and_VirtualTag_same_equipment_and_name_collide()
|
||||
{
|
||||
var draft = new DraftSnapshot
|
||||
{
|
||||
GenerationId = 1, ClusterId = "c",
|
||||
Tags = [BuildTag(equipmentId: "eq-1", name: "speed", folderPath: null)],
|
||||
VirtualTags = [BuildVirtualTag(equipmentId: "eq-1", name: "speed")],
|
||||
};
|
||||
|
||||
DraftValidator.Validate(draft).ShouldContain(e => e.Code == "EquipmentSignalNameCollision");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a Tag in a sub-folder does not collide with a VirtualTag at the
|
||||
/// equipment root even when the names match — the NodeId keys differ.</summary>
|
||||
[Fact]
|
||||
public void Same_name_different_folder_does_not_collide()
|
||||
{
|
||||
var draft = new DraftSnapshot
|
||||
{
|
||||
GenerationId = 1, ClusterId = "c",
|
||||
Tags = [BuildTag(equipmentId: "eq-1", name: "speed", folderPath: "metrics")],
|
||||
VirtualTags = [BuildVirtualTag(equipmentId: "eq-1", name: "speed")],
|
||||
};
|
||||
|
||||
DraftValidator.Validate(draft).ShouldNotContain(e => e.Code == "EquipmentSignalNameCollision");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a SystemPlatform Tag (EquipmentId == null) is excluded from the
|
||||
/// equipment-signal node space and so never collides with an equipment VirtualTag.</summary>
|
||||
[Fact]
|
||||
public void SystemPlatform_tag_sharing_name_with_equipment_vtag_excluded()
|
||||
{
|
||||
var draft = new DraftSnapshot
|
||||
{
|
||||
GenerationId = 1, ClusterId = "c",
|
||||
Tags = [BuildTag(equipmentId: null, name: "speed", folderPath: null)],
|
||||
VirtualTags = [BuildVirtualTag(equipmentId: "eq-1", name: "speed")],
|
||||
};
|
||||
|
||||
DraftValidator.Validate(draft).ShouldNotContain(e => e.Code == "EquipmentSignalNameCollision");
|
||||
}
|
||||
|
||||
private static Tag BuildTag(string? equipmentId, string name, string? folderPath) => new()
|
||||
{
|
||||
TagId = $"tag-{name}",
|
||||
DriverInstanceId = "d",
|
||||
EquipmentId = equipmentId,
|
||||
Name = name,
|
||||
FolderPath = folderPath,
|
||||
DataType = "Float",
|
||||
AccessLevel = TagAccessLevel.Read,
|
||||
TagConfig = "{}",
|
||||
};
|
||||
|
||||
private static VirtualTag BuildVirtualTag(string equipmentId, string name) => new()
|
||||
{
|
||||
VirtualTagId = $"vtag-{name}",
|
||||
EquipmentId = equipmentId,
|
||||
Name = name,
|
||||
DataType = "Float",
|
||||
ScriptId = "s-1",
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
// Phase 6.3 task #148 part 2 — ValidateClusterTopology
|
||||
// ------------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user