Files
lmxopcua/tests/Drivers/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests/OpcUaClientDriverFactoryTests.cs
T
Joseph Doherty ec01649905 v3(b1-opcuaclient): re-key OpcUaClient resolution to RawPath via RawTags
NamespaceMap now carries a RawPath -> upstream node id table built from the
deployed RawTagEntry list (reads each tag's TagConfig.nodeId, threads
WriteIdempotent). Under v3 the read/write/subscribe/history reference handed to
the driver is the tag's RawPath identity; the driver resolves it in two stages:
RawPath -> nodeId string (instance TryResolve) -> live NodeId re-bound against
the session (static TryResolve). Alarm ConditionId + event-history sourceName
stay on the direct session parse (they are upstream node ids, not RawPaths).

- Options: add IReadOnlyList<RawTagEntry> RawTags (Contracts now refs Core.Abstractions).
- Factory: RawTags binds straight into options (no separate DTO); EndpointUrl kept.
- Browser: unchanged (emits neutral BrowseNode DTOs, no TagConfig FullName key).
- Tests: 138 green; new RawPath resolution + factory-binding coverage; migrated
  the stale-session ReadRaw test to author a resolving RawTag.

Contracts + Driver + Browser build clean (0 warn). Wave C wires endpoint->DeviceConfig
and the deploy artifact that populates RawTags.
2026-07-15 20:11:44 -04:00

79 lines
3.7 KiB
C#

using Shouldly;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit;
namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
/// <summary>
/// Tests for <see cref="OpcUaClientDriverFactoryExtensions"/> — the factory that lets the
/// Server-side <c>DriverFactoryBootstrap</c> materialise a real <see cref="OpcUaClientDriver"/>
/// from a <c>DriverInstance</c> row instead of falling back to a stub.
/// </summary>
public class OpcUaClientDriverFactoryTests
{
private const string SampleConfig =
"""{"EndpointUrl":"opc.tcp://host:4840","SecurityMode":"None","AutoAcceptCertificates":true}""";
/// <summary>Verifies the factory builds a driver carrying the right type + instance identity.</summary>
[Fact]
public void CreateInstance_builds_an_OpcUaClient_driver_with_the_right_identity()
{
var driver = OpcUaClientDriverFactoryExtensions.CreateInstance("drv-1", SampleConfig, NullLoggerFactory.Instance);
driver.DriverType.ShouldBe("OpcUaClient");
driver.DriverInstanceId.ShouldBe("drv-1");
}
/// <summary>Verifies the public driver-type-name constant matches the driver's DriverType.</summary>
[Fact]
public void DriverTypeName_is_OpcUaClient()
=> OpcUaClientDriverFactoryExtensions.DriverTypeName.ShouldBe("OpcUaClient");
/// <summary>Verifies a JSON literal that deserialises to null is rejected with a clear error.</summary>
[Fact]
public void CreateInstance_throws_on_null_json_deserialisation()
=> Should.Throw<System.InvalidOperationException>(
() => OpcUaClientDriverFactoryExtensions.CreateInstance("drv-1", "null", NullLoggerFactory.Instance));
private const string RawTagsConfig =
"""
{
"EndpointUrl": "opc.tcp://host:4840",
"RawTags": [
{ "rawPath": "Plant/Line1/Temp", "tagConfig": "{\"nodeId\":\"ns=2;s=Temperature\"}", "writeIdempotent": false },
{ "rawPath": "Plant/Line1/Speed", "tagConfig": "{\"nodeId\":\"ns=2;s=Speed\"}", "writeIdempotent": true }
]
}
""";
/// <summary>
/// Verifies the v3 authored <c>RawTags</c> (RawPath + TagConfig blob + WriteIdempotent) bind
/// through the DriverConfig deserialiser directly onto <see cref="OpcUaClientDriverOptions"/> —
/// the OpcUaClient driver has no separate DTO, so the factory's direct-into-options path must
/// populate the RawPath resolution list.
/// </summary>
[Fact]
public void CreateInstance_binds_rawtags_from_driverconfig()
{
var driver = OpcUaClientDriverFactoryExtensions.CreateInstance("drv-raw", RawTagsConfig, NullLoggerFactory.Instance);
// The factory succeeds and identity is intact — the deserialiser tolerated + bound the RawTags array.
driver.DriverType.ShouldBe("OpcUaClient");
driver.DriverInstanceId.ShouldBe("drv-raw");
// Assert the actual binding: deserialise the same DriverConfig into public options with the
// factory's serialiser settings (case-insensitive + string enums) and confirm RawTags populated.
var opts = System.Text.Json.JsonSerializer.Deserialize<OpcUaClientDriverOptions>(
RawTagsConfig,
new System.Text.Json.JsonSerializerOptions
{
PropertyNameCaseInsensitive = true,
Converters = { new System.Text.Json.Serialization.JsonStringEnumConverter() },
});
opts.ShouldNotBeNull();
opts!.RawTags.Count.ShouldBe(2);
opts.RawTags[0].RawPath.ShouldBe("Plant/Line1/Temp");
opts.RawTags[0].TagConfig.ShouldContain("ns=2;s=Temperature");
opts.RawTags[0].WriteIdempotent.ShouldBeFalse();
opts.RawTags[1].WriteIdempotent.ShouldBeTrue();
}
}