From 53eb3fcda51c4d3ff5e5243c5891bf2137c3a713 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 8 Jun 2026 06:56:22 -0400 Subject: [PATCH] test(config): DraftValidator accepts driver-less equipment + driverless equipment namespace --- .../DraftValidatorTests.cs | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/DraftValidatorTests.cs b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/DraftValidatorTests.cs index ad769f92..6e98af3b 100644 --- a/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/DraftValidatorTests.cs +++ b/tests/Core/ZB.MOM.WW.OtOpcUa.Configuration.Tests/DraftValidatorTests.cs @@ -297,6 +297,65 @@ public sealed class DraftValidatorTests string.Join("; ", errors.Select(e => $"[{e.Code}] {e.Message}"))); } + // ------------------------------------------------------------------------------------ + // Driver-less Equipment namespace — core safety claim (feat/driverless-equipment-namespace) + // ------------------------------------------------------------------------------------ + + /// Characterisation test: a draft that contains an Equipment-kind namespace with + /// ZERO rows, and a single whose + /// is , must pass + /// with no errors. This locks in the design's core + /// safety claim — the validator must never implicitly require a driver for Equipment-namespace + /// equipment. + [Fact] + public void Validate_accepts_driverless_equipment_in_driverless_equipment_namespace() + { + // An Equipment-kind namespace — no DriverInstance rows at all for this namespace. + var eqNamespace = new Namespace + { + NamespaceId = "MAIN-OPCUA-equipment", + ClusterId = "MAIN", + Kind = NamespaceKind.Equipment, + NamespaceUri = "urn:zb:main:equipment", + }; + + // UNS topology required by ValidateUnsSegments / ValidatePathLength. + var area = new UnsArea { UnsAreaId = "area-filling", ClusterId = "MAIN", Name = "filling" }; + var line = new UnsLine { UnsLineId = "line-1", UnsAreaId = area.UnsAreaId, Name = "line-1" }; + + // Canonical EquipmentId derived from UUID — satisfies ValidateEquipmentIdDerivation. + var uuid = Guid.NewGuid(); + var equipment = new Equipment + { + EquipmentUuid = uuid, + EquipmentId = DraftValidator.DeriveEquipmentId(uuid), + Name = "rinser-01", + DriverInstanceId = null, // ← driver-less: the property under test + UnsLineId = line.UnsLineId, + MachineCode = "machine_001", + ZTag = null, + SAPID = null, + }; + + var draft = new DraftSnapshot + { + GenerationId = 0, + ClusterId = string.Empty, // global snapshot — matches DraftSnapshotFactory.FromConfigDbAsync + Namespaces = [eqNamespace], // Equipment namespace present + DriverInstances = [], // ← zero drivers: the other half of the safety claim + UnsAreas = [area], + UnsLines = [line], + Equipment = [equipment], + }; + + var errors = DraftValidator.Validate(draft); + + errors.ShouldBeEmpty( + "a driver-less Equipment in an Equipment-kind namespace with no DriverInstances must pass " + + "all validator rules; firing rules: " + + string.Join("; ", errors.Select(e => $"[{e.Code}] {e.Message}"))); + } + // ------------------------------------------------------------------------------------ // ValidateNoEquipmentSignalNameCollision — Tag/VirtualTag NodeId collision // ------------------------------------------------------------------------------------