74 lines
3.2 KiB
C#
74 lines
3.2 KiB
C#
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Protocol;
|
|
using ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests.Adapters;
|
|
|
|
/// <summary>
|
|
/// Tests for <see cref="RealOpcUaClient.BrowseChildrenAsync"/>.
|
|
///
|
|
/// Two shapes here:
|
|
///
|
|
/// 1. A live round-trip against the infra OPC UA server
|
|
/// (<c>opc.tcp://localhost:50000</c> — see <c>infra/docker-compose.yml</c>,
|
|
/// started via <c>cd infra && docker compose up -d opcua</c>).
|
|
/// Marked <c>[SkippableFact]</c> so it reports Skipped — not failed — on
|
|
/// machines that don't have the infra stack running. The live test asserts
|
|
/// that the server root browse returns the standard "Server" node, which
|
|
/// proves we targeted ObjectsFolder (<c>ns=0;i=85</c>) and that the
|
|
/// response mapping survived the round trip.
|
|
///
|
|
/// 2. A pure unit test that exercises the not-connected guard — no infra
|
|
/// needed, runs in every build.
|
|
/// </summary>
|
|
[Trait("Category", "RequiresOpcUa")]
|
|
public class RealOpcUaClientBrowseTests
|
|
{
|
|
// The infra/docker-compose.yml opcua container maps the OPC PLC simulator
|
|
// on host port 50000 (not the OPC UA default 4840). Matches what the
|
|
// existing docker/ and docker-env2/ topologies dial into.
|
|
private const string EndpointUrl = "opc.tcp://localhost:50000";
|
|
|
|
[SkippableFact]
|
|
public async Task BrowseChildren_at_root_returns_ObjectsFolder_with_Server_node()
|
|
{
|
|
await using var client = new RealOpcUaClient();
|
|
|
|
// Probe the endpoint before asserting anything. If the infra OPC UA
|
|
// server isn't up, ConnectAsync surfaces a socket/timeout error from
|
|
// deep inside the OPC Foundation SDK — we treat that as "infra not
|
|
// available" and skip rather than fail, mirroring the SkippableFact
|
|
// pattern already used in ConfigurationDatabase/AuditLog tests.
|
|
try
|
|
{
|
|
await client.ConnectAsync(
|
|
EndpointUrl,
|
|
new OpcUaConnectionOptions(AutoAcceptUntrustedCerts: true));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Skip.If(true, $"OPC UA test server not reachable on {EndpointUrl}: {ex.Message}");
|
|
return; // Skip.If throws; this is unreachable but keeps the compiler happy.
|
|
}
|
|
|
|
var result = await client.BrowseChildrenAsync(parentNodeId: null);
|
|
|
|
// Under ObjectsFolder (ns=0;i=85) every OPC UA-compliant server
|
|
// exposes a 'Server' object at ns=0;i=2253 — its presence confirms
|
|
// we hit the right root and that DisplayName mapping survives the
|
|
// round trip.
|
|
Assert.NotEmpty(result.Children);
|
|
Assert.Contains(result.Children, n => n.DisplayName == "Server");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BrowseChildren_throws_ConnectionNotConnected_when_session_is_null()
|
|
{
|
|
// No ConnectAsync — _session is still null, so the typed guard at the
|
|
// top of BrowseChildrenAsync should fire before any SDK call.
|
|
await using var client = new RealOpcUaClient();
|
|
|
|
await Assert.ThrowsAsync<ConnectionNotConnectedException>(
|
|
() => client.BrowseChildrenAsync(parentNodeId: null));
|
|
}
|
|
}
|