Files
ScadaBridge/tests/ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests/Adapters/RealOpcUaClientBrowseTests.cs
T

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 &amp;&amp; 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));
}
}