test(runtime): comms-loss → identical-value recovery contract + silent-resubscribe control (03/S15)

This commit is contained in:
Joseph Doherty
2026-08-20 10:27:48 -04:00
parent 7d98ef9332
commit ae97b32c82
4 changed files with 278 additions and 6 deletions
@@ -9,6 +9,7 @@ using ZB.MOM.WW.OtOpcUa.Commons.Types;
using ZB.MOM.WW.OtOpcUa.Configuration;
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
using ZB.MOM.WW.OtOpcUa.Configuration.Enums;
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
using ZB.MOM.WW.OtOpcUa.Runtime.Drivers;
using ZB.MOM.WW.OtOpcUa.Runtime.OpcUa;
@@ -36,16 +37,20 @@ public static class V3ArtifactSeeder
/// <param name="rev">The revision hash the deployment is sealed under.</param>
/// <param name="rawTags">The raw tags to materialise, one per (driver, device, tag).</param>
/// <param name="unsRefs">UNS references projecting a raw tag (by <c>BackingTag</c>) into an equipment.</param>
/// <param name="driversEnabled">Whether the seeded DriverInstance rows are Enabled — i.e. whether the
/// host SPAWNS a child around them. False (the default) keeps the routing suites child-free; a suite
/// that needs a real <c>DriverInstanceActor</c> sets it and supplies a driver factory.</param>
/// <returns>The seeded deployment's id.</returns>
public static DeploymentId SeedV3Deployment(
IDbContextFactory<OtOpcUaConfigDbContext> db, RevisionHash rev,
(string Driver, string DriverName, string Device, string Tag, string DataType, bool Writable)[] rawTags,
(string Area, string Line, string Equip, string BackingTag, string? Effective)[] unsRefs)
(string Area, string Line, string Equip, string BackingTag, string? Effective)[] unsRefs,
bool driversEnabled = false)
{
var drivers = rawTags
.Select(t => (t.Driver, t.DriverName))
.Distinct()
.Select(d => new { DriverInstanceId = d.Driver, RawFolderId = "rf-plant", Name = d.DriverName, DriverType = "Modbus", DriverConfig = "{}", ClusterId = "c1", Enabled = false })
.Select(d => new { DriverInstanceId = d.Driver, RawFolderId = "rf-plant", Name = d.DriverName, DriverType = "Modbus", DriverConfig = "{}", ClusterId = "c1", Enabled = driversEnabled })
.ToArray();
var devices = rawTags
.Select(t => (t.Driver, t.Device))
@@ -117,14 +122,18 @@ public static class V3ArtifactSeeder
/// <param name="commsRecoveryAuditDelay">Optional 03/S15 audit delay — how long after a reconnect the
/// host checks for refs still latched <c>BadNoCommunication</c>. Null keeps the production default,
/// which is far longer than any test's lifetime.</param>
/// <param name="driverFactory">Optional factory the host builds its driver children from. Null (the
/// default) leaves the null factory, which — with <c>driversEnabled: false</c> — means no child is
/// spawned at all.</param>
/// <returns>The host actor plus the publish and mux probes wired into it.</returns>
public static (IActorRef Actor, TestProbe Publish, TestProbe Mux) SpawnHostAndApply(
TestKitBase kit, IDbContextFactory<OtOpcUaConfigDbContext> db,
NodeId localNode, DeploymentId deploymentId, RevisionHash rev,
TimeSpan? commsRecoveryAuditDelay = null)
TimeSpan? commsRecoveryAuditDelay = null,
IDriverFactory? driverFactory = null)
{
var publish = kit.CreateTestProbe();
var (actor, mux, coordinator) = Spawn(kit, db, localNode, publish.Ref, commsRecoveryAuditDelay);
var (actor, mux, coordinator) = Spawn(kit, db, localNode, publish.Ref, commsRecoveryAuditDelay, driverFactory);
Dispatch(actor, coordinator, deploymentId, rev);
publish.ExpectMsg<OpcUaPublishActor.RebuildAddressSpace>(RuntimeActorTestBase.PresenceBudget);
@@ -151,7 +160,8 @@ public static class V3ArtifactSeeder
private static (IActorRef Actor, TestProbe Mux, TestProbe Coordinator) Spawn(
TestKitBase kit, IDbContextFactory<OtOpcUaConfigDbContext> db, NodeId localNode,
IActorRef? opcUaPublishActor, TimeSpan? commsRecoveryAuditDelay = null)
IActorRef? opcUaPublishActor, TimeSpan? commsRecoveryAuditDelay = null,
IDriverFactory? driverFactory = null)
{
var coordinator = kit.CreateTestProbe();
var mux = kit.CreateTestProbe();
@@ -159,6 +169,7 @@ public static class V3ArtifactSeeder
var actor = kit.Sys.ActorOf(DriverHostActor.Props(
db, localNode, coordinator.Ref,
driverFactory: driverFactory,
localRoles: new HashSet<string> { "driver" },
dependencyMux: mux.Ref,
opcUaPublishActor: opcUaPublishActor,