From f2006adb791b7570b3de5c53e8cf68ae9e4aa13e Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Mon, 13 Jul 2026 09:55:04 -0400 Subject: [PATCH] =?UTF-8?q?test(runtime):=20pin=20VT=20inputs-ready=20gate?= =?UTF-8?q?=20=E2=80=94=20no=20Bad=20flash=20before=20all=20dependencies?= =?UTF-8?q?=20arrive=20(02/S13)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...2-03-vt-failure-quality-plan.md.tasks.json | 2 +- .../VirtualTags/VirtualTagActorTests.cs | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/archreview/plans/R2-03-vt-failure-quality-plan.md.tasks.json b/archreview/plans/R2-03-vt-failure-quality-plan.md.tasks.json index 1ec95919..ec8ebe41 100644 --- a/archreview/plans/R2-03-vt-failure-quality-plan.md.tasks.json +++ b/archreview/plans/R2-03-vt-failure-quality-plan.md.tasks.json @@ -34,7 +34,7 @@ { "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)", - "status": "pending", + "status": "completed", "blockedBy": [ "R2-03-T3" ] diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/VirtualTagActorTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/VirtualTagActorTests.cs index 1a9913ce..a63d9ed5 100644 --- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/VirtualTagActorTests.cs +++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/VirtualTags/VirtualTagActorTests.cs @@ -142,6 +142,44 @@ public sealed class VirtualTagActorTests : RuntimeActorTestBase parent.ExpectMsg().Quality.ShouldBe(OpcUaQuality.Bad); } + /// 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. + [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)); + } + + /// 02/S13 inputs-ready gate: once every declared dependency has arrived at least once, a + /// failure publishes exactly one Bad EvaluationResult. + [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().Quality.ShouldBe(OpcUaQuality.Bad); + } + /// Test evaluator returning a fixed sequence of results, one per Evaluate call; the last /// result repeats once the queue is drained. private sealed class QueuedEvaluator : IVirtualTagEvaluator