diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorTests.cs
index 7dfc2fc3..2429e610 100644
--- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorTests.cs
+++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Drivers/DriverHostActorTests.cs
@@ -157,6 +157,48 @@ public sealed class DriverHostActorTests : RuntimeActorTestBase
plan.PredicateSource.ShouldContain("ctx.GetTag");
}
+ /// Bootstrap-restore: a node that already has an Applied NodeDeploymentState
+ /// row for a ScriptedAlarm-carrying deployment re-forwards the
+ /// on PreStart (no dispatch needed), so a
+ /// restarted node restores its live ScriptedAlarm children.
+ [Fact]
+ public void Restore_on_bootstrap_forwards_EquipmentScriptedAlarms_to_scripted_alarm_host()
+ {
+ var db = NewInMemoryDbFactory();
+ var deploymentId = SeedDeploymentWithScriptedAlarm(db, RevA);
+
+ // Seed an Applied NodeDeploymentState row so Bootstrap() detects the Applied branch and
+ // calls RestoreApplied — no DispatchDeployment needed.
+ using (var ctx = db.CreateDbContext())
+ {
+ ctx.NodeDeploymentStates.Add(new Configuration.Entities.NodeDeploymentState
+ {
+ NodeId = TestNode.Value,
+ DeploymentId = deploymentId.Value,
+ Status = NodeDeploymentStatus.Applied,
+ StartedAtUtc = DateTime.UtcNow,
+ AppliedAtUtc = DateTime.UtcNow,
+ });
+ ctx.SaveChanges();
+ }
+
+ var coordinator = CreateTestProbe();
+ var alarmHost = CreateTestProbe();
+ // No DispatchDeployment — Bootstrap() should detect the Applied row and run RestoreApplied,
+ // which routes through PushDesiredSubscriptions and forwards ApplyScriptedAlarms.
+ Sys.ActorOf(DriverHostActor.Props(
+ db, TestNode, coordinator.Ref,
+ localRoles: new HashSet { "driver" },
+ scriptedAlarmHostOverride: alarmHost.Ref));
+
+ var apply = alarmHost.ExpectMsg(TimeSpan.FromSeconds(5));
+ var plan = apply.Plans.ShouldHaveSingleItem();
+ plan.ScriptedAlarmId.ShouldBe("al-1");
+ plan.EquipmentId.ShouldBe("eq-1");
+ plan.Name.ShouldBe("Overheat");
+ plan.PredicateSource.ShouldContain("ctx.GetTag");
+ }
+
private static DeploymentId SeedDeploymentWithScriptedAlarm(
IDbContextFactory db, RevisionHash rev)
{