Files
lmxopcua/tests/ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.IntegrationTests/OpcPlcReverseConnectFixture.cs
2026-04-26 06:08:30 -04:00

57 lines
2.6 KiB
C#

namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.IntegrationTests;
/// <summary>
/// Fixture for the reverse-connect variant of opc-plc (PR-11). Unlike the
/// dial-mode <see cref="OpcPlcFixture"/>, the simulator here is the dialer:
/// it reaches OUT to the test runner's listener URL on
/// <c>opc.tcp://host.docker.internal:4844</c>. The fixture's job is to
/// advertise the listener URL the test should bind and provide a clear
/// skip reason when the docker-compose service isn't running.
/// </summary>
/// <remarks>
/// <para>
/// <b>Why no port-probe</b>: the conventional fixture probes the simulator's
/// server port to detect docker-up. In reverse-connect the simulator opens
/// no inbound port — it's a pure dialer — so a probe would always fail.
/// Tests that want a hard skip should look at <see cref="SkipReason"/>
/// which is set from the <c>OPCUA_RC_SIM</c> env var (any value =
/// "simulator running"; absent = skip).
/// </para>
/// <para>
/// The "shared listener URL" model is enforced by the driver's
/// <c>ReverseConnectListener</c> singleton — multiple smoke tests in the
/// same xunit assembly share one listener instance even if they run in
/// parallel. Tests should pick distinct <c>ExpectedServerUri</c> values to
/// demultiplex inbound connections.
/// </para>
/// </remarks>
public sealed class OpcPlcReverseConnectFixture : IAsyncDisposable
{
private const string DefaultListenerUrl = "opc.tcp://0.0.0.0:4844";
private const string EnvVar = "OPCUA_RC_SIM";
/// <summary>The listener URL the driver should bind for incoming reverse-connect dials.</summary>
public string ListenerUrl { get; } = DefaultListenerUrl;
/// <summary>Skip reason when the reverse-connect simulator isn't available; null when ready.</summary>
public string? SkipReason { get; }
public OpcPlcReverseConnectFixture()
{
if (string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(EnvVar)))
{
SkipReason =
$"Reverse-connect smoke skipped — set {EnvVar}=1 once `docker compose -f Docker/docker-compose.yml up opc-plc-rc` is healthy. " +
"The dialer needs host.docker.internal to reach this machine — verify Docker Desktop's network mode supports it.";
}
}
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
}
[Xunit.CollectionDefinition(Name)]
public sealed class OpcPlcReverseConnectCollection : Xunit.ICollectionFixture<OpcPlcReverseConnectFixture>
{
public const string Name = "OpcPlcReverseConnect";
}