diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/ScriptAnalysis/InboundScriptHost.cs b/src/ZB.MOM.WW.ScadaBridge.CentralUI/ScriptAnalysis/InboundScriptHost.cs index fda5d3d4..607f42bb 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/ScriptAnalysis/InboundScriptHost.cs +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/ScriptAnalysis/InboundScriptHost.cs @@ -98,5 +98,21 @@ public class InboundScriptHost IReadOnlyDictionary attributeValues, System.Threading.CancellationToken cancellationToken = default) => System.Threading.Tasks.Task.CompletedTask; + + /// + /// Waits until a named attribute on the target instance reaches the specified value, + /// or until the timeout elapses. + /// + /// The name of the attribute to watch. + /// The value to wait for. + /// Maximum time to wait before returning false. + /// Cancellation token. + /// A task that resolves to true if the attribute reached the target value within the timeout, false otherwise. + public System.Threading.Tasks.Task WaitForAttribute( + string attributeName, + object? targetValue, + System.TimeSpan timeout, + System.Threading.CancellationToken cancellationToken = default) => + System.Threading.Tasks.Task.FromResult(false); } } diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/ScriptAnalysis/SandboxInboundScriptHost.cs b/src/ZB.MOM.WW.ScadaBridge.CentralUI/ScriptAnalysis/SandboxInboundScriptHost.cs index 639a0f40..2c155945 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/ScriptAnalysis/SandboxInboundScriptHost.cs +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/ScriptAnalysis/SandboxInboundScriptHost.cs @@ -104,6 +104,21 @@ public class SandboxInboundScriptHost CancellationToken cancellationToken = default) => throw Unavailable("SetAttributes(...)"); + /// + /// Always throws ; cross-site routing is unavailable in a Test Run. + /// + /// Attribute name (included in the exception message). + /// Unused target value. + /// Unused timeout. + /// Unused token. + /// Never returns; always throws . + public Task WaitForAttribute( + string attributeName, + object? targetValue, + TimeSpan timeout, + CancellationToken cancellationToken = default) => + throw Unavailable($"WaitForAttribute(\"{attributeName}\")"); + private ScriptSandboxException Unavailable(string operation) => new($"Route.To(\"{_instanceCode}\").{operation} is not available in Test Run — " + "cross-site routing needs a deployed site reachable over the cluster transport."); diff --git a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/ScriptAnalysis/ScriptAnalysisServiceTests.cs b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/ScriptAnalysis/ScriptAnalysisServiceTests.cs index 4b7500d3..a044e39a 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/ScriptAnalysis/ScriptAnalysisServiceTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests/ScriptAnalysis/ScriptAnalysisServiceTests.cs @@ -644,4 +644,20 @@ public class ScriptAnalysisServiceTests Assert.All(lines, l => Assert.Equal($"S{i}", l.Trim())); } } + + // ── Inbound-script analysis surface ────────────────────────────────── + + [Fact] + public void InboundScript_WaitForAttribute_DiagnosesClean() + { + // WaitForAttribute is a shipped inbound-script helper. The editor must + // not flag it as an error: the InboundScriptHost mirror must expose the + // method so Roslyn resolves it during static analysis. + var resp = _svc.Diagnose(new DiagnoseRequest( + Code: "var ok = await Route.To(\"site-a\").WaitForAttribute(\"Flag\", true, System.TimeSpan.FromSeconds(5));", + Kind: ScriptKind.InboundApi)); + + Assert.DoesNotContain(resp.Markers, m => m.Code.StartsWith("CS")); + Assert.DoesNotContain(resp.Markers, m => m.Code.StartsWith("SCADA")); + } }