From 7b2f64fdb8e5183f1e5d9044479dd0d422fe638d Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Sun, 7 Jun 2026 03:12:09 -0400 Subject: [PATCH] refactor(runtime): case-insensitive ClusterId/NodeId match + suppress short-circuit + edge tests (review) --- .../Drivers/DeploymentArtifact.cs | 13 +++---- .../Drivers/DeploymentArtifactTests.cs | 34 +++++++++++++++++++ 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DeploymentArtifact.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DeploymentArtifact.cs index 84b8966a..12e5adf5 100644 --- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DeploymentArtifact.cs +++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Drivers/DeploymentArtifact.cs @@ -109,7 +109,7 @@ public static class DeploymentArtifact { if (el.ValueKind != JsonValueKind.Object) continue; var nid = el.TryGetProperty("NodeId", out var nEl) ? nEl.GetString() : null; - if (!string.Equals(nid, nodeId, StringComparison.Ordinal)) continue; + if (!string.Equals(nid, nodeId, StringComparison.OrdinalIgnoreCase)) continue; myCluster = el.TryGetProperty("ClusterId", out var cEl) ? cEl.GetString() : null; break; } @@ -136,14 +136,11 @@ public static class DeploymentArtifact public static IReadOnlyList ParseDriverInstances(ReadOnlySpan blob, string nodeId) { var scope = ResolveClusterScope(blob, nodeId); + if (scope.Mode == ClusterFilterMode.Suppress) return Array.Empty(); var all = ParseDriverInstances(blob); - return scope.Mode switch - { - ClusterFilterMode.Suppress => Array.Empty(), - ClusterFilterMode.ScopeTo => all.Where( - s => string.Equals(s.ClusterId, scope.ClusterId, StringComparison.Ordinal)).ToArray(), - _ => all, - }; + return scope.Mode == ClusterFilterMode.ScopeTo + ? all.Where(s => string.Equals(s.ClusterId, scope.ClusterId, StringComparison.OrdinalIgnoreCase)).ToArray() + : all; } private static DriverInstanceSpec? TryReadSpec(JsonElement el) diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DeploymentArtifactTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DeploymentArtifactTests.cs index 42686a16..aab6757c 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DeploymentArtifactTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DeploymentArtifactTests.cs @@ -295,4 +295,38 @@ public sealed class DeploymentArtifactTests specs.Single().DriverInstanceId.ShouldBe("DI-ok"); } + + /// Verifies that a malformed blob resolves to None rather than throwing. + [Fact] + public void ResolveClusterScope_malformed_blob_returns_None() + { + var scope = DeploymentArtifact.ResolveClusterScope("not json"u8.ToArray(), "central-1:4053"); + scope.Mode.ShouldBe(ClusterFilterMode.None); + } + + /// Verifies that a blank ClusterId in the node row resolves to Suppress. + [Fact] + public void ResolveClusterScope_blank_cluster_id_suppresses() + { + var blob = BlobOf(new + { + Clusters = new[] { new { ClusterId = "MAIN" }, new { ClusterId = "SITE-A" } }, + Nodes = new[] { new { NodeId = "central-1:4053", ClusterId = "" } }, + }); + DeploymentArtifact.ResolveClusterScope(blob, "central-1:4053").Mode.ShouldBe(ClusterFilterMode.Suppress); + } + + /// Verifies that NodeId matching in ResolveClusterScope is case-insensitive. + [Fact] + public void ResolveClusterScope_node_id_match_is_case_insensitive() + { + var blob = BlobOf(new + { + Clusters = new[] { new { ClusterId = "MAIN" }, new { ClusterId = "SITE-A" } }, + Nodes = new[] { new { NodeId = "Central-1:4053", ClusterId = "MAIN" } }, + }); + var scope = DeploymentArtifact.ResolveClusterScope(blob, "central-1:4053"); + scope.Mode.ShouldBe(ClusterFilterMode.ScopeTo); + scope.ClusterId.ShouldBe("MAIN"); + } }