test(runtime): pin VT inputs-ready gate — no Bad flash before all dependencies arrive (02/S13)
This commit is contained in:
@@ -34,7 +34,7 @@
|
|||||||
{
|
{
|
||||||
"id": "R2-03-T5",
|
"id": "R2-03-T5",
|
||||||
"subject": "S13 cold-start suppression guards: missing-dep failure publishes no Bad; failure after all deps arrived publishes Bad (inputs-ready gate)",
|
"subject": "S13 cold-start suppression guards: missing-dep failure publishes no Bad; failure after all deps arrived publishes Bad (inputs-ready gate)",
|
||||||
"status": "pending",
|
"status": "completed",
|
||||||
"blockedBy": [
|
"blockedBy": [
|
||||||
"R2-03-T3"
|
"R2-03-T3"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -142,6 +142,44 @@ public sealed class VirtualTagActorTests : RuntimeActorTestBase
|
|||||||
parent.ExpectMsg<VirtualTagActor.EvaluationResult>().Quality.ShouldBe(OpcUaQuality.Bad);
|
parent.ExpectMsg<VirtualTagActor.EvaluationResult>().Quality.ShouldBe(OpcUaQuality.Bad);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>02/S13 inputs-ready gate: a multi-dep script that fails before all its declared
|
||||||
|
/// dependencies have arrived publishes NO Bad — the node stays at the materialiser's
|
||||||
|
/// BadWaitingForInitialData, avoiding a false Bad flash during deploy/respawn warm-up.</summary>
|
||||||
|
[Fact]
|
||||||
|
public void Cold_start_failure_with_missing_dependency_publishes_no_Bad()
|
||||||
|
{
|
||||||
|
var parent = CreateTestProbe();
|
||||||
|
var actor = parent.ChildActorOf(VirtualTagActor.Props(
|
||||||
|
"vt-1", "expr",
|
||||||
|
evaluator: new FailingEvaluator("boom"),
|
||||||
|
dependencyRefs: new[] { "a", "b" }));
|
||||||
|
|
||||||
|
// Only "a" has arrived — "b" is still missing, so the gate suppresses the Bad publish.
|
||||||
|
actor.Tell(new VirtualTagActor.DependencyValueChanged("a", 1, DateTime.UtcNow));
|
||||||
|
|
||||||
|
parent.ExpectNoMsg(TimeSpan.FromMilliseconds(300));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>02/S13 inputs-ready gate: once every declared dependency has arrived at least once, a
|
||||||
|
/// failure publishes exactly one Bad EvaluationResult.</summary>
|
||||||
|
[Fact]
|
||||||
|
public void Failure_after_all_dependencies_arrived_publishes_Bad()
|
||||||
|
{
|
||||||
|
var parent = CreateTestProbe();
|
||||||
|
var actor = parent.ChildActorOf(VirtualTagActor.Props(
|
||||||
|
"vt-1", "expr",
|
||||||
|
evaluator: new FailingEvaluator("boom"),
|
||||||
|
dependencyRefs: new[] { "a", "b" }));
|
||||||
|
|
||||||
|
actor.Tell(new VirtualTagActor.DependencyValueChanged("a", 1, DateTime.UtcNow));
|
||||||
|
// Still missing "b" — no publish yet.
|
||||||
|
parent.ExpectNoMsg(TimeSpan.FromMilliseconds(200));
|
||||||
|
|
||||||
|
// "b" arrives — all refs now seen, so the failure degrades to Bad.
|
||||||
|
actor.Tell(new VirtualTagActor.DependencyValueChanged("b", 2, DateTime.UtcNow));
|
||||||
|
parent.ExpectMsg<VirtualTagActor.EvaluationResult>().Quality.ShouldBe(OpcUaQuality.Bad);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Test evaluator returning a fixed sequence of results, one per Evaluate call; the last
|
/// <summary>Test evaluator returning a fixed sequence of results, one per Evaluate call; the last
|
||||||
/// result repeats once the queue is drained.</summary>
|
/// result repeats once the queue is drained.</summary>
|
||||||
private sealed class QueuedEvaluator : IVirtualTagEvaluator
|
private sealed class QueuedEvaluator : IVirtualTagEvaluator
|
||||||
|
|||||||
Reference in New Issue
Block a user