Replaces the Ok=true stub with a TCP connect to the peer's OPC UA port (4840
default) with a 2s timeout. A successful connect indicates the OPC UA server
process is up + accepting connections — enough for the redundancy calculator
to treat the peer as live. A full secure-channel Hello/Acknowledge handshake
is overkill for what the redundancy calc consumes and would pull in the OPC
UA Client SDK + a PKI setup. Upgrade later if a deeper liveness signal is ever
required.
Probe extracts the host from NodeId by stripping the :port suffix (commit
5cfbe8b encoded host:port into NodeId for cluster-member identity).
Tests: 2 new tests — Ok=true against a live TcpListener on a chosen port,
Ok=false against an unreachable endpoint. All 17 Runtime tests pass (was 16
covering only the message-contract surface).
75 lines
2.8 KiB
C#
75 lines
2.8 KiB
C#
using System.Net;
|
|
using System.Net.Sockets;
|
|
using Akka.Actor;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Commons.Types;
|
|
using ZB.MOM.WW.OtOpcUa.Runtime.Health;
|
|
using ZB.MOM.WW.OtOpcUa.Runtime.Historian;
|
|
using ZB.MOM.WW.OtOpcUa.Runtime.Tests.Harness;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Health;
|
|
|
|
public sealed class HealthProbeActorTests : RuntimeActorTestBase
|
|
{
|
|
[Fact]
|
|
public async Task DbHealthProbeActor_returns_reachable_against_in_memory_db()
|
|
{
|
|
var db = NewInMemoryDbFactory();
|
|
var actor = Sys.ActorOf(DbHealthProbeActor.Props(db));
|
|
|
|
var status = await actor.Ask<DbHealthProbeActor.DbHealthStatus>(
|
|
DbHealthProbeActor.GetStatus.Instance, TimeSpan.FromSeconds(3));
|
|
|
|
status.Reachable.ShouldBeTrue();
|
|
status.LastError.ShouldBeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void PeerOpcUaProbeActor_reports_Ok_true_against_a_live_listener()
|
|
{
|
|
using var listener = new TcpListener(IPAddress.Loopback, 0);
|
|
listener.Start();
|
|
var port = ((IPEndPoint)listener.LocalEndpoint).Port;
|
|
|
|
var received = new System.Collections.Generic.List<object>();
|
|
Sys.ActorOf(PeerOpcUaProbeActor.Props(
|
|
NodeId.Parse($"127.0.0.1:{port}"),
|
|
interval: TimeSpan.FromMilliseconds(50),
|
|
connectTimeout: TimeSpan.FromMilliseconds(500),
|
|
opcUaPort: port,
|
|
broadcast: msg => received.Add(msg)));
|
|
|
|
AwaitCondition(() => received.OfType<PeerOpcUaProbeActor.OpcUaProbeResult>().Any(r => r.Ok),
|
|
TimeSpan.FromSeconds(3));
|
|
}
|
|
|
|
[Fact]
|
|
public void PeerOpcUaProbeActor_reports_Ok_false_against_an_unreachable_endpoint()
|
|
{
|
|
// Port 1 is reserved (tcpmux) and almost never bound on dev machines, so the connect fails fast.
|
|
var received = new System.Collections.Generic.List<object>();
|
|
Sys.ActorOf(PeerOpcUaProbeActor.Props(
|
|
NodeId.Parse("127.0.0.1:1"),
|
|
interval: TimeSpan.FromMilliseconds(50),
|
|
connectTimeout: TimeSpan.FromMilliseconds(300),
|
|
opcUaPort: 1,
|
|
broadcast: msg => received.Add(msg)));
|
|
|
|
AwaitCondition(() => received.OfType<PeerOpcUaProbeActor.OpcUaProbeResult>().Any(r => !r.Ok),
|
|
TimeSpan.FromSeconds(3));
|
|
}
|
|
|
|
[Fact]
|
|
public void HistorianAdapterActor_buffers_rows()
|
|
{
|
|
var actor = Sys.ActorOf(HistorianAdapterActor.Props());
|
|
for (var i = 0; i < 5; i++)
|
|
actor.Tell(new HistorianAdapterActor.HistoryRow("driver-a", $"tag-{i}", i, DateTime.UtcNow));
|
|
|
|
ExpectNoMsg(TimeSpan.FromMilliseconds(100));
|
|
// No direct readback of the count from a sealed actor — assert by Ask of a self-probe later
|
|
// when the engine wiring lands (F11). For now this asserts the actor accepts the contract.
|
|
}
|
|
}
|