test(opcuaclient-browser): skip live browse tests when opc-plc is unreachable; move out of unit project (#594)

Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
Joseph Doherty
2026-10-08 06:48:34 -04:00
parent b37e9613f7
commit 7e91c37a52
3 changed files with 63 additions and 8 deletions
@@ -1,76 +0,0 @@
using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.Commons.Browsing;
using ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser;
namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Browser.Tests;
/// <summary>
/// Live-server tests against the opc-plc Docker fixture. Bring up with
/// `lmxopcua-fix up opcuaclient` from PowerShell before running. These tests
/// drive the internal <c>OpcUaClientBrowseSession</c> through its public factory
/// <see cref="OpcUaClientDriverBrowser"/> so no InternalsVisibleTo is needed.
/// Filter out with <c>--filter "Category!=RequiresOpcPlc"</c> when the fixture
/// is unreachable.
/// </summary>
[Trait("Category", "RequiresOpcPlc")]
public sealed class OpcUaClientBrowseSessionTests
{
private static string Endpoint =>
Environment.GetEnvironmentVariable("OPCUA_SIM_ENDPOINT") ?? "opc.tcp://10.100.0.35:50000";
// SecurityPolicy.None, SecurityMode.None, OpcUaAuthType.Anonymous are all
// index 0 and can also be written as string names now that the browser carries
// JsonStringEnumConverter (Driver.OpcUaClient.Browser-003). Numeric ordinals are
// kept here as they were before the fix — both forms are accepted.
private static string ConfigJson => $$"""
{
"EndpointUrl":"{{Endpoint}}",
"SecurityPolicy":0,
"SecurityMode":0,
"AuthType":0,
"SessionTimeout":"00:01:00",
"Timeout":"00:00:10",
"PerEndpointConnectTimeout":"00:00:10"
}
""";
/// <summary>RootAsync should surface at least one node under ObjectsFolder
/// (opc-plc exposes a non-empty top level).</summary>
[Fact]
public async Task RootAsync_returns_at_least_one_node()
{
var ct = TestContext.Current.CancellationToken;
var browser = new OpcUaClientDriverBrowser();
await using var session = await browser.OpenAsync(ConfigJson, ct);
var roots = await session.RootAsync(ct);
roots.Count.ShouldBeGreaterThan(0);
}
/// <summary>ExpandAsync should accept a stable NodeId returned by RootAsync
/// and round-trip through the live namespace table.</summary>
[Fact]
public async Task ExpandAsync_round_trips_stable_NodeId()
{
var ct = TestContext.Current.CancellationToken;
var browser = new OpcUaClientDriverBrowser();
await using var session = await browser.OpenAsync(ConfigJson, ct);
var roots = await session.RootAsync(ct);
var folder = roots.FirstOrDefault(n => n.Kind == BrowseNodeKind.Folder);
folder.ShouldNotBeNull("expected at least one Folder under ObjectsFolder");
var children = await session.ExpandAsync(folder!.NodeId, ct);
children.ShouldNotBeNull();
}
/// <summary>The OPC UA picker treats variables as terminal leaves, so
/// AttributesAsync must always be empty for this driver.</summary>
[Fact]
public async Task AttributesAsync_is_empty_for_opcuaclient()
{
var ct = TestContext.Current.CancellationToken;
var browser = new OpcUaClientDriverBrowser();
await using var session = await browser.OpenAsync(ConfigJson, ct);
var attrs = await session.AttributesAsync("nsu=http://opcfoundation.org/UA/;i=85", ct);
attrs.ShouldBeEmpty();
}
}