test(otopcua): cover discovered-node rebind drop + clarify re-apply scope
This commit is contained in:
@@ -341,6 +341,50 @@ public sealed class DriverHostActorDiscoveryTests : RuntimeActorTestBase
|
||||
publish.ExpectNoMsg(TimeSpan.FromMilliseconds(500));
|
||||
}
|
||||
|
||||
/// <summary>Task 8 rebind-guard: a redeploy that REBINDS the driver to a DIFFERENT equipment must DROP the
|
||||
/// cached discovered plan rather than re-graft EQ-1-scoped nodes under EQ-2. d1 still resolves to exactly
|
||||
/// one equipment (so the Count==0 drop does NOT fire), but the cached plan's NodeIds are scoped to the OLD
|
||||
/// equipment (EQ-1), so the <c>StartsWith(equipmentId + "/")</c> guard sees they no longer match EQ-2 and
|
||||
/// drops the entry. After the redeploy NO <see cref="OpcUaPublishActor.MaterialiseDiscoveredNodes"/> is
|
||||
/// re-told. (Complements <see cref="Discovered_nodes_dropped_when_equipment_no_longer_resolves"/>, which
|
||||
/// covers the Count==0 branch; this covers the rebind/StartsWith branch.)</summary>
|
||||
[Fact]
|
||||
public void Discovered_nodes_dropped_when_driver_rebound_to_a_different_equipment()
|
||||
{
|
||||
var db = NewInMemoryDbFactory();
|
||||
var factory = new SubscribingDriverFactory("Modbus");
|
||||
var deploymentId = SeedDeploymentWithEquipmentTags(db, RevA,
|
||||
(Equip: "EQ-1", Driver: "d1", FullName: "40001", Folder: (string?)null, Name: "speed"));
|
||||
|
||||
var (actor, publish, coordinator) = SpawnHostAndApply(db, deploymentId, factory);
|
||||
|
||||
actor.Tell(new DriverInstanceActor.DiscoveredNodesReady("d1", new[]
|
||||
{
|
||||
new DiscoveredNode(
|
||||
FolderPathSegments: new[] { "FOCAS", "10.0.0.5:8193", "Identity" },
|
||||
BrowseName: "Model", DisplayName: "Model", FullReference: "ft-ref-1",
|
||||
DataType: DriverDataType.Float64, IsArray: false, ArrayDim: null,
|
||||
Writable: false, IsHistorized: false),
|
||||
}));
|
||||
|
||||
// First injection materialises under EQ-1 (the cached plan's NodeIds are scoped to EQ-1).
|
||||
publish.ExpectMsg<OpcUaPublishActor.MaterialiseDiscoveredNodes>(Timeout);
|
||||
|
||||
// Apply a SECOND deployment where d1 is REBOUND to a DIFFERENT equipment EQ-2 (d1 still present + still
|
||||
// resolves to exactly one equipment, but the cached plan is scoped to EQ-1). The DriverConfig is
|
||||
// unchanged ("{}") so ReconcileDrivers does NOT restart d1 — exactly the config-unchanged rebind the
|
||||
// guard's known-limitation comment describes.
|
||||
var deploymentId2 = SeedDeploymentWithEquipmentTags(db, RevB,
|
||||
(Equip: "EQ-2", Driver: "d1", FullName: "40001", Folder: (string?)null, Name: "speed"));
|
||||
actor.Tell(new DispatchDeployment(deploymentId2, RevB, CorrelationId.NewId()));
|
||||
coordinator.ExpectMsg<ApplyAck>(Timeout).Outcome.ShouldBe(ApplyAckOutcome.Applied);
|
||||
|
||||
// After draining the fresh RebuildAddressSpace, NO MaterialiseDiscoveredNodes is re-told — the cached
|
||||
// EQ-1-scoped plan was dropped by the rebind guard (its NodeId no longer starts with "EQ-2/").
|
||||
publish.ExpectMsg<OpcUaPublishActor.RebuildAddressSpace>(Timeout);
|
||||
publish.ExpectNoMsg(TimeSpan.FromMilliseconds(500));
|
||||
}
|
||||
|
||||
/// <summary>Spawns the host with the subscribing driver factory + a publish probe, dispatches the
|
||||
/// deployment, and waits for the Applied ACK so the apply (and thus <c>_lastComposition</c> + the live
|
||||
/// child + the initial SubscribeBulk pass) has completed before the test injects discovered nodes. A
|
||||
|
||||
Reference in New Issue
Block a user