fix(vtags): prune _planByVtag on child termination + crash-then-change test (H1b review follow-up)
This commit is contained in:
@@ -243,6 +243,40 @@ public sealed class VirtualTagHostActorTests : RuntimeActorTestBase
|
||||
mux.ExpectNoMsg(TimeSpan.FromMilliseconds(400));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// H1b crash-then-change: after a child terminates unexpectedly AND the next apply carries a
|
||||
/// CHANGED plan for the same vtagId, the replacement child must adopt the NEW plan (refs "B"),
|
||||
/// not the stale one (refs "A"). This pins that OnChildTerminated prunes _planByVtag so the
|
||||
/// change-detect guard can't be confused by a dead child's old plan.
|
||||
/// </summary>
|
||||
[Fact]
|
||||
public void Respawn_after_termination_with_a_changed_plan_adopts_the_new_refs()
|
||||
{
|
||||
var publish = CreateTestProbe();
|
||||
var mux = CreateTestProbe();
|
||||
var host = Sys.ActorOf(VirtualTagHostActor.Props(publish.Ref, mux.Ref, new StubEvaluator()));
|
||||
|
||||
// First apply — child registers interest in "A".
|
||||
host.Tell(new VirtualTagHostActor.ApplyVirtualTags(
|
||||
new[] { PlanWithRefs("vt-1", "eq-1", "speed-rpm", "ctx.GetTag(\"A\")", "A") }));
|
||||
mux.ExpectMsg<DependencyMuxActor.RegisterInterest>().TagRefs.ShouldContain("A");
|
||||
var firstChild = mux.LastSender;
|
||||
|
||||
// Kill the child deterministically and drain its PostStop UnregisterInterest.
|
||||
Watch(firstChild);
|
||||
Sys.Stop(firstChild);
|
||||
ExpectTerminated(firstChild);
|
||||
mux.ExpectMsg<DependencyMuxActor.UnregisterInterest>(TimeSpan.FromSeconds(5));
|
||||
|
||||
// Re-apply with a CHANGED plan (refs "B"). The replacement must register the NEW refs.
|
||||
host.Tell(new VirtualTagHostActor.ApplyVirtualTags(
|
||||
new[] { PlanWithRefs("vt-1", "eq-1", "speed-rpm", "ctx.GetTag(\"B\")", "B") }));
|
||||
var reg2 = mux.ExpectMsg<DependencyMuxActor.RegisterInterest>(TimeSpan.FromSeconds(5));
|
||||
reg2.TagRefs.ShouldContain("B");
|
||||
reg2.TagRefs.ShouldNotContain("A");
|
||||
mux.LastSender.ShouldNotBe(firstChild);
|
||||
}
|
||||
|
||||
/// <summary>Deterministic no-op evaluator: keeps spawned children inert so tests drive the host's
|
||||
/// OnResult path directly via synthetic EvaluationResults.</summary>
|
||||
private sealed class StubEvaluator : IVirtualTagEvaluator
|
||||
|
||||
Reference in New Issue
Block a user