4624141a5d
tag gate, and fix three silently-broken OpcUaClient integration tests
Continues working through deferment.md's remaining items.
SKIPPED TESTS (13 -> 0 for this reason). The skip said "dark until Batch 4";
Batch 4 shipped as v3.0 and RETIRED the equipment-tag path rather than lighting
it, so every one of them was waiting on something that had already happened and
gone the other way. Resolved individually rather than in bulk:
- REVIVED onto the raw/UNS shape (6): four DriverHostActor primary-gate cases,
the cluster-scoping rebuild, and the real-SDK dual-namespace materialisation
E2E. The behaviour never went dark, only the v2 seeding did.
- FOLDED into a live parity test (3): DeploymentArtifactRawUnsParityTests
already compared RawTagPlan record-equal and RawTagPlan carries array /
historize / alarm intent, so widening its corpus by two tags covers what
three empty placeholder suites had been promising. Those suites are deleted.
- DELETED (4): subjects genuinely retired -- equipment-namespace EquipmentTags,
and both dot-joint {{equip}}.X token suites (that resolver was deleted in
Batch 3; the slash-joint form is covered by DeploymentArtifactEquipRefParityTests).
Falsified, not assumed: removing the seeded UnsTagReference turns two revived
primary-gate tests red; re-homing the SITE-A node to MAIN turns the scoping test
red. Widening a parity corpus also needed direct value assertions -- parity alone
cannot distinguish "both seams right" from "both seams blind".
Runtime.Tests skips 13->3, OpcUaServer.Tests 4->1; all remaining are deliberate
EquipmentDeviceBindingRetired tombstones.
G-7 (deploy-time TagConfig gate). MQTT shipped an Inspect() nobody ever called;
now wired. Replaced the hand-maintained list with a reflection guard, because the
existing test enumerates driver types by hand and so guards renames but can never
notice a gap. NOTE: the first version of that guard was hollow -- it discovered
candidates via GetReferencedAssemblies(), which is circular, since the compiler
elides a reference nothing in the IL uses. Removing MQTT from the map also removed
its Contracts assembly from the manifest, and the guard passed green with the bug
reintroduced. Rewritten to scan the output directory; re-falsified and it now
fails naming MqttTagDefinitionFactory. Residual recorded at the code: Sql,
MTConnect, Calculation and OpcUaClient have no Contracts-side Inspect at all, so
the deploy gate stays weaker than the AdminUI's authoring gate for them.
OPCUACLIENT INTEGRATION TESTS (3 of 4 failing, on master too). Not fixture rot:
the simulator was healthy and Client.CLI read the very same node Good. v3 made the
driver resolve every reference through its authored RawTags table, so a bare
"ns=3;s=StepUp" fails LOCALLY at the resolver with BadNodeIdInvalid and never
reaches the wire -- the tests were exercising the unresolved-reference guard.
Fixed by seeding OpcPlcProfile.RawTags in the deploy artifact's TagConfig shape
and addressing by RawPath. 4/4 pass.
DOCS. Applied the Phase7* -> AddressSpace* rename across the four live docs
(historical bannered files deliberately untouched), resolving the three that are
not renames to their real members. Found en route that the earlier banner pass
missed three files: VirtualTags.md, OpcUaServer.md and ScriptedAlarms.md all
still presented the test-scaffolding GenericDriverNodeManager as the production
dispatch path. All three now carry the correction.
Claude-Session: https://claude.ai/code/session_015p7wGqy3YpZNCpDzTpGMKo
80 lines
4.3 KiB
C#
80 lines
4.3 KiB
C#
using System.Text.Json;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.IntegrationTests;
|
|
|
|
/// <summary>
|
|
/// Driver-side configuration + well-known opc-plc node identifiers that the smoke
|
|
/// tests address. Node IDs are stable across opc-plc releases — the simulator
|
|
/// guarantees the same <c>ns=3;s=...</c> names shipped since v1.0. If a release
|
|
/// bump breaks these, the fixture's pinned image tag needs a coordinated bump.
|
|
/// <para><b>These tests address the driver by RawPath, not by node id (fixed 2026-07-28).</b> v3
|
|
/// made the driver resolve every read/write/subscribe reference through its authored
|
|
/// <see cref="OpcUaClientDriverOptions.RawTags"/> table — a bare <c>ns=3;s=StepUp</c> is no longer a
|
|
/// reference the driver accepts, and one that does not resolve is failed LOCALLY as
|
|
/// <c>BadNodeIdInvalid</c> with no wire round-trip. The smoke tests still passed bare node ids, so all
|
|
/// three had been failing against a perfectly healthy simulator. Seeding <c>RawTags</c> here is what
|
|
/// makes them exercise the driver instead of its unresolved-reference guard.</para>
|
|
/// </summary>
|
|
public static class OpcPlcProfile
|
|
{
|
|
/// <summary>opc-plc monotonically-increasing UInt32; ticks once per second under default opts.</summary>
|
|
public const string StepUp = "ns=3;s=StepUp";
|
|
|
|
/// <summary>opc-plc random Int32 node; new value ~every 100ms.</summary>
|
|
public const string RandomSignedInt32 = "ns=3;s=RandomSignedInt32";
|
|
|
|
/// <summary>opc-plc alternating boolean; flips every second.</summary>
|
|
public const string AlternatingBoolean = "ns=3;s=AlternatingBoolean";
|
|
|
|
/// <summary>opc-plc fast uint node — ticks every 100ms. Used for subscription-cadence tests.</summary>
|
|
public const string FastUInt1 = "ns=3;s=FastUInt1";
|
|
|
|
/// <summary>The RawPath the smoke tests address <see cref="StepUp"/> by.</summary>
|
|
public const string StepUpRef = "Sim/OpcUaClient/plc/StepUp";
|
|
|
|
/// <summary>The RawPath the smoke tests address <see cref="RandomSignedInt32"/> by.</summary>
|
|
public const string RandomSignedInt32Ref = "Sim/OpcUaClient/plc/RandomSignedInt32";
|
|
|
|
/// <summary>The RawPath the smoke tests address <see cref="AlternatingBoolean"/> by.</summary>
|
|
public const string AlternatingBooleanRef = "Sim/OpcUaClient/plc/AlternatingBoolean";
|
|
|
|
/// <summary>The RawPath the smoke tests address <see cref="FastUInt1"/> by.</summary>
|
|
public const string FastUInt1Ref = "Sim/OpcUaClient/plc/FastUInt1";
|
|
|
|
/// <summary>The authored raw-tag table the driver resolves the <c>*Ref</c> RawPaths through — the
|
|
/// same <c>{"nodeId": …}</c> TagConfig shape the deploy artifact delivers.</summary>
|
|
public static IReadOnlyList<RawTagEntry> RawTags { get; } =
|
|
[
|
|
Entry(StepUpRef, StepUp),
|
|
Entry(RandomSignedInt32Ref, RandomSignedInt32),
|
|
Entry(AlternatingBooleanRef, AlternatingBoolean),
|
|
Entry(FastUInt1Ref, FastUInt1),
|
|
];
|
|
|
|
private static RawTagEntry Entry(string rawPath, string nodeId) =>
|
|
new(rawPath, JsonSerializer.Serialize(new { nodeId }), WriteIdempotent: true);
|
|
|
|
/// <summary>Builds driver options for the OPC PLC endpoint.</summary>
|
|
/// <param name="endpointUrl">The endpoint URL of the OPC PLC simulator.</param>
|
|
/// <returns>Configured driver options for the OPC PLC.</returns>
|
|
public static OpcUaClientDriverOptions BuildOptions(string endpointUrl) => new()
|
|
{
|
|
RawTags = RawTags,
|
|
EndpointUrl = endpointUrl,
|
|
SecurityPolicy = OpcUaSecurityPolicy.None,
|
|
SecurityMode = OpcUaSecurityMode.None,
|
|
AuthType = OpcUaAuthType.Anonymous,
|
|
// opc-plc auto-accepts client certs (--aa) but we still present one; trust the
|
|
// server's cert back since the simulator regenerates it each container spin-up
|
|
// and there's no meaningful chain to validate against.
|
|
AutoAcceptCertificates = true,
|
|
Timeout = TimeSpan.FromSeconds(10),
|
|
SessionTimeout = TimeSpan.FromSeconds(30),
|
|
// opc-plc's standard address space mirrors verbatim — treat it as SystemPlatform so
|
|
// §8 namespace validation passes without requiring a UNS mapping table.
|
|
TargetNamespaceKind = OpcUaTargetNamespaceKind.SystemPlatform,
|
|
};
|
|
}
|