173 lines
5.7 KiB
C#
173 lines
5.7 KiB
C#
using System;
|
|
using System.Threading.Tasks;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.LmxOpcUa.Host.Configuration;
|
|
using ZB.MOM.WW.LmxOpcUa.Host.Domain;
|
|
using ZB.MOM.WW.LmxOpcUa.Host.Metrics;
|
|
using ZB.MOM.WW.LmxOpcUa.Host.MxAccess;
|
|
using ZB.MOM.WW.LmxOpcUa.Tests.Helpers;
|
|
|
|
namespace ZB.MOM.WW.LmxOpcUa.Tests.MxAccess
|
|
{
|
|
/// <summary>
|
|
/// Verifies the background connectivity monitor used to reconnect the MXAccess bridge after faults or stale probes.
|
|
/// </summary>
|
|
public class MxAccessClientMonitorTests : IDisposable
|
|
{
|
|
private readonly StaComThread _staThread;
|
|
private readonly FakeMxProxy _proxy;
|
|
private readonly PerformanceMetrics _metrics;
|
|
|
|
/// <summary>
|
|
/// Initializes the monitor test fixture with a shared STA thread, fake proxy, and metrics collector.
|
|
/// </summary>
|
|
public MxAccessClientMonitorTests()
|
|
{
|
|
_staThread = new StaComThread();
|
|
_staThread.Start();
|
|
_proxy = new FakeMxProxy();
|
|
_metrics = new PerformanceMetrics();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disposes the monitor test fixture resources.
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
_staThread.Dispose();
|
|
_metrics.Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Confirms that the monitor reconnects the client after an observed disconnect.
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task Monitor_ReconnectsOnDisconnect()
|
|
{
|
|
var config = new MxAccessConfiguration
|
|
{
|
|
MonitorIntervalSeconds = 1,
|
|
AutoReconnect = true
|
|
};
|
|
var client = new MxAccessClient(_staThread, _proxy, config, _metrics);
|
|
|
|
await client.ConnectAsync();
|
|
await client.DisconnectAsync();
|
|
|
|
client.StartMonitor();
|
|
|
|
// Wait for monitor to detect disconnect and reconnect
|
|
await Task.Delay(2500);
|
|
|
|
client.StopMonitor();
|
|
client.State.ShouldBe(ConnectionState.Connected);
|
|
client.ReconnectCount.ShouldBeGreaterThan(0);
|
|
client.Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Confirms that the monitor can be started and stopped without throwing.
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task Monitor_StopsOnCancel()
|
|
{
|
|
var config = new MxAccessConfiguration { MonitorIntervalSeconds = 1 };
|
|
var client = new MxAccessClient(_staThread, _proxy, config, _metrics);
|
|
|
|
await client.ConnectAsync();
|
|
client.StartMonitor();
|
|
client.StopMonitor();
|
|
|
|
// Should not throw
|
|
await Task.Delay(200);
|
|
client.Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Confirms that a stale probe tag triggers a reconnect when monitoring is enabled.
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task Monitor_ProbeStale_ForcesReconnect()
|
|
{
|
|
var config = new MxAccessConfiguration
|
|
{
|
|
ProbeTag = "TestProbe",
|
|
ProbeStaleThresholdSeconds = 2,
|
|
MonitorIntervalSeconds = 1,
|
|
AutoReconnect = true
|
|
};
|
|
var client = new MxAccessClient(_staThread, _proxy, config, _metrics);
|
|
|
|
await client.ConnectAsync();
|
|
client.StartMonitor();
|
|
|
|
// Wait long enough for probe to go stale (threshold=2s, monitor interval=1s)
|
|
// No data changes simulated → probe becomes stale → reconnect triggered
|
|
await Task.Delay(4000);
|
|
|
|
client.StopMonitor();
|
|
client.ReconnectCount.ShouldBeGreaterThan(0);
|
|
client.Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Confirms that fresh probe updates prevent unnecessary reconnects.
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task Monitor_ProbeDataChange_PreventsStaleReconnect()
|
|
{
|
|
var config = new MxAccessConfiguration
|
|
{
|
|
ProbeTag = "TestProbe",
|
|
ProbeStaleThresholdSeconds = 2,
|
|
MonitorIntervalSeconds = 1,
|
|
AutoReconnect = true
|
|
};
|
|
var client = new MxAccessClient(_staThread, _proxy, config, _metrics);
|
|
|
|
await client.ConnectAsync();
|
|
client.StartMonitor();
|
|
|
|
// Continuously simulate probe data changes to keep it fresh
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
await Task.Delay(500);
|
|
_proxy.SimulateDataChangeByAddress("TestProbe", i, 192);
|
|
}
|
|
|
|
client.StopMonitor();
|
|
// Probe was kept fresh → no reconnect should have happened
|
|
client.ReconnectCount.ShouldBe(0);
|
|
client.State.ShouldBe(ConnectionState.Connected);
|
|
client.Dispose();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Confirms that enabling the monitor without a probe tag does not trigger false reconnects.
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task Monitor_NoProbeConfigured_NoFalseReconnect()
|
|
{
|
|
var config = new MxAccessConfiguration
|
|
{
|
|
ProbeTag = null, // No probe
|
|
MonitorIntervalSeconds = 1,
|
|
AutoReconnect = true
|
|
};
|
|
var client = new MxAccessClient(_staThread, _proxy, config, _metrics);
|
|
|
|
await client.ConnectAsync();
|
|
client.StartMonitor();
|
|
|
|
// Wait several monitor cycles — should stay connected with no reconnects
|
|
await Task.Delay(3000);
|
|
|
|
client.StopMonitor();
|
|
client.State.ShouldBe(ConnectionState.Connected);
|
|
client.ReconnectCount.ShouldBe(0);
|
|
client.Dispose();
|
|
}
|
|
}
|
|
}
|