refactor(opcuaclient): remove vestigial TargetNamespaceKind + UnsMappingTable (#648)
Nothing read the mapping table beyond the startup validator; v3 UNS projection is
explicit via UnsTagReference. Legacy keys in stored DriverConfig are ignored (pinned).
Reverts the docker-dev opc-plc seed workaround from fdee129f.
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
+42
-15
@@ -13,21 +13,21 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
|
||||
public sealed class OpcUaClientReinitConfigAdoptionTests
|
||||
{
|
||||
/// <summary>
|
||||
/// <c>ValidateNamespaceKind</c> runs at the top of init and rejects <c>TargetNamespaceKind=Equipment</c>
|
||||
/// with an empty UNS mapping table. Reinitializing INTO that shape must therefore throw — a driver
|
||||
/// still serving the constructor's valid options would validate those and succeed, which is exactly
|
||||
/// how the discarded edit stayed invisible.
|
||||
/// <c>ValidateTuning</c> runs at the top of init and rejects a non-absolute <c>discoveryUrl</c>.
|
||||
/// Reinitializing INTO that shape must therefore throw — a driver still serving the constructor's
|
||||
/// valid options would validate those and succeed, which is exactly how the discarded edit stayed
|
||||
/// invisible.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public async Task Reinitialize_adopts_a_changed_config()
|
||||
{
|
||||
var driver = OpcUaClientDriverFactoryExtensions.CreateInstance(
|
||||
"opc1",
|
||||
"""{"endpointUrl":"opc.tcp://10.0.0.1:4840","targetNamespaceKind":"Equipment","unsMappingTable":{"a":"b"}}""");
|
||||
"""{"endpointUrl":"opc.tcp://127.0.0.1:1"}""");
|
||||
|
||||
await Should.ThrowAsync<InvalidOperationException>(
|
||||
() => driver.ReinitializeAsync(
|
||||
"""{"endpointUrl":"opc.tcp://10.0.0.1:4840","targetNamespaceKind":"Equipment","unsMappingTable":{}}""",
|
||||
"""{"endpointUrl":"opc.tcp://127.0.0.1:1","discoveryUrl":"not a uri"}""",
|
||||
CancellationToken.None),
|
||||
"OpcUaClientDriver kept its constructor-supplied options after a reinitialize with a changed config (#516)");
|
||||
}
|
||||
@@ -36,15 +36,42 @@ public sealed class OpcUaClientReinitConfigAdoptionTests
|
||||
[Fact]
|
||||
public async Task Reinitialize_with_an_empty_document_keeps_the_constructor_options()
|
||||
{
|
||||
var driver = OpcUaClientDriverFactoryExtensions.CreateInstance(
|
||||
"opc1",
|
||||
"""{"endpointUrl":"opc.tcp://10.0.0.1:4840","targetNamespaceKind":"Equipment","unsMappingTable":{"a":"b"}}""");
|
||||
// Built directly (the factory's parse would refuse the bad discoveryUrl up front).
|
||||
using var driver = new OpcUaClientDriver(
|
||||
new OpcUaClientDriverOptions { EndpointUrl = "opc.tcp://127.0.0.1:1", DiscoveryUrl = "not a uri" }, "opc1");
|
||||
|
||||
// "{}" keeps the (valid) constructor options, so ValidateNamespaceKind passes and the driver gets as
|
||||
// far as the connect — which fails against an unreachable endpoint. Anything BUT the validation
|
||||
// throw proves the constructor options survived.
|
||||
var ex = await Record.ExceptionAsync(() => driver.ReinitializeAsync("{}", CancellationToken.None));
|
||||
(ex is InvalidOperationException ioe && ioe.Message.Contains("UnsMappingTable", StringComparison.OrdinalIgnoreCase))
|
||||
.ShouldBeFalse("an empty document must keep the constructor-supplied options, not re-validate an empty config");
|
||||
// The constructor options are invalid (bad discoveryUrl) but "{}" keeps them WITHOUT re-parsing;
|
||||
// a re-parse of an empty config would yield valid defaults. So tuning validation must still see
|
||||
// the constructor's bad discoveryUrl and throw — a driver that re-parsed "{}" would not.
|
||||
var ex = await Should.ThrowAsync<InvalidOperationException>(
|
||||
() => driver.ReinitializeAsync("{}", CancellationToken.None));
|
||||
ex.Message.ShouldContain("DiscoveryUrl");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #648 — <c>targetNamespaceKind</c> / <c>unsMappingTable</c> were removed (vestigial in v3). DriverConfig
|
||||
/// JSON stored before the removal still carries them, in every shape the old validator used to refuse
|
||||
/// (Equipment with no table, Equipment with a table, SystemPlatform with a table). None may fail the
|
||||
/// parse or the initialise: the unknown keys are ignored. Falsifiable — it fails if a namespace-kind
|
||||
/// validator is reinstated, because the Equipment-without-table blob would throw again.
|
||||
/// </summary>
|
||||
[Theory]
|
||||
[InlineData("""{"endpointUrl":"opc.tcp://127.0.0.1:1","targetNamespaceKind":"Equipment"}""")]
|
||||
[InlineData("""{"endpointUrl":"opc.tcp://127.0.0.1:1","targetNamespaceKind":"Equipment","unsMappingTable":{"a":"b"}}""")]
|
||||
[InlineData("""{"endpointUrl":"opc.tcp://127.0.0.1:1","targetNamespaceKind":"SystemPlatform","unsMappingTable":{"a":"b"}}""")]
|
||||
[InlineData("""{"endpointUrl":"opc.tcp://127.0.0.1:1","targetNamespaceKind":1,"unsMappingTable":{}}""")]
|
||||
public async Task Legacy_namespace_keys_are_ignored_by_parse_and_initialise(string legacyJson)
|
||||
{
|
||||
Should.NotThrow(() => OpcUaClientDriverFactoryExtensions.ParseOptions("opc1", legacyJson));
|
||||
|
||||
var driver = OpcUaClientDriverFactoryExtensions.CreateInstance("opc1", legacyJson);
|
||||
|
||||
// Initialise gets past config validation and fails only at the connect (nothing listens on port 1).
|
||||
// Anything other than a namespace-validation InvalidOperationException proves the keys were ignored.
|
||||
var ex = await Record.ExceptionAsync(() => driver.InitializeAsync(legacyJson, CancellationToken.None));
|
||||
(ex is InvalidOperationException ioe
|
||||
&& (ioe.Message.Contains("UnsMappingTable", StringComparison.OrdinalIgnoreCase)
|
||||
|| ioe.Message.Contains("TargetNamespaceKind", StringComparison.OrdinalIgnoreCase)))
|
||||
.ShouldBeFalse("legacy namespace keys must be ignored, not validated");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user