Merge R2-11 TagConfig consolidation (arch-review round 2) [PR #434]
v2-ci / build (push) Successful in 3m55s
v2-ci / unit-tests (push) Failing after 8m14s

Findings 01/C-1, 01/P-1 (single TagConfigIntent.Parse in Commons, 4 copies
delegate, parse-once) + 05 CONV-2/UNDER-1/UNDER-6 (shared strict TagConfigJson
readers, per-driver Inspect() + writable key, FOCAS capability pre-flight,
Modbus probe timeoutMs, deploy-gate Warn|Error). Phase C runtime-strict flip
deferred. T22/T24 deferred. Auto-merged AddressSpaceApplier.cs + DriverHostActor.cs
with R2-04/R2-10; verified OpcUaServer.Tests 286/286 + Runtime.Tests 396/396.
Build clean.
This commit is contained in:
Joseph Doherty
2026-07-13 11:29:32 -04:00
74 changed files with 2228 additions and 682 deletions
@@ -0,0 +1,36 @@
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Core.OpcUa;
namespace ZB.MOM.WW.OtOpcUa.Core.Tests.OpcUa;
/// <summary>
/// Characterization net (R2-11) pinning <see cref="EquipmentNodeWalker.ExtractFullName"/>'s
/// raw-blob-fallback semantics across the golden TagConfig corpus BEFORE the parser is delegated to
/// <c>TagConfigIntent.Parse</c>. The walker's FullName copy falls back to the raw blob whenever the
/// input is not a JSON object carrying a string <c>FullName</c> (blank / non-object / malformed /
/// absent / non-string). Whitespace + non-string-property values are returned verbatim (not trimmed).
/// Must be green on the unmodified tree and stay green through the swap.
/// </summary>
public sealed class EquipmentNodeWalkerFullNameCorpusTests
{
[Theory]
// object with a string FullName → the FullName string (verbatim, incl. whitespace)
[InlineData("{\"FullName\":\"X.Y\"}", "X.Y")]
[InlineData("{\"FullName\":\" \"}", " ")]
[InlineData("{\"FullName\":\"A.B\",\"region\":\"Coils\",\"address\":5}", "A.B")]
// blank → raw blob (IsNullOrWhiteSpace short-circuit)
[InlineData(" ", " ")]
// non-object root → raw blob
[InlineData("[1,2]", "[1,2]")]
// malformed JSON → raw blob
[InlineData("not json {", "not json {")]
// object, FullName absent → raw blob
[InlineData("{\"region\":\"HoldingRegisters\",\"address\":5}", "{\"region\":\"HoldingRegisters\",\"address\":5}")]
// object, FullName present but non-string → raw blob
[InlineData("{\"FullName\":123}", "{\"FullName\":123}")]
public void ExtractFullName_falls_back_to_raw_blob_except_for_string_FullName(string tagConfig, string expected)
{
EquipmentNodeWalker.ExtractFullName(tagConfig).ShouldBe(expected);
}
}