feat(runtime): HistorianAdapter + PeerOpcUaProbe + DbHealthProbe actors (engine wiring tracked as F11/F12)

This commit is contained in:
Joseph Doherty
2026-05-26 05:09:06 -04:00
parent e115f13104
commit 28639cb14d
4 changed files with 194 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
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_publishes_probe_result_at_each_tick()
{
var received = new System.Collections.Generic.List<object>();
var actor = Sys.ActorOf(PeerOpcUaProbeActor.Props(
NodeId.Parse("peer-1"),
interval: TimeSpan.FromMilliseconds(50),
broadcast: msg => received.Add(msg)));
AwaitCondition(() => received.Count >= 2, TimeSpan.FromSeconds(2));
received.OfType<PeerOpcUaProbeActor.OpcUaProbeResult>().ShouldNotBeEmpty();
}
[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.
}
}