fix(central-ui): mirror WaitForAttribute on inbound-script analysis RouteTarget

Add WaitForAttribute(attributeName, targetValue, timeout, cancellationToken)
to InboundScriptHost.RouteTarget and SandboxInboundScriptHost.RouteTarget,
mirroring the shipped runtime signature in RouteHelper. Eliminates the false
CS error the editor raised against valid Route.To("X").WaitForAttribute(...)
calls in inbound API method scripts. Test asserts the call diagnoses clean
under ScriptKind.InboundApi.
This commit is contained in:
Joseph Doherty
2026-06-17 11:04:13 -04:00
parent a1186685a9
commit bee295d3ee
3 changed files with 47 additions and 0 deletions
@@ -98,5 +98,21 @@ public class InboundScriptHost
IReadOnlyDictionary<string, string> attributeValues, IReadOnlyDictionary<string, string> attributeValues,
System.Threading.CancellationToken cancellationToken = default) => System.Threading.CancellationToken cancellationToken = default) =>
System.Threading.Tasks.Task.CompletedTask; System.Threading.Tasks.Task.CompletedTask;
/// <summary>
/// Waits until a named attribute on the target instance reaches the specified value,
/// or until the timeout elapses.
/// </summary>
/// <param name="attributeName">The name of the attribute to watch.</param>
/// <param name="targetValue">The value to wait for.</param>
/// <param name="timeout">Maximum time to wait before returning false.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>A task that resolves to true if the attribute reached the target value within the timeout, false otherwise.</returns>
public System.Threading.Tasks.Task<bool> WaitForAttribute(
string attributeName,
object? targetValue,
System.TimeSpan timeout,
System.Threading.CancellationToken cancellationToken = default) =>
System.Threading.Tasks.Task.FromResult(false);
} }
} }
@@ -104,6 +104,21 @@ public class SandboxInboundScriptHost
CancellationToken cancellationToken = default) => CancellationToken cancellationToken = default) =>
throw Unavailable("SetAttributes(...)"); throw Unavailable("SetAttributes(...)");
/// <summary>
/// Always throws <see cref="ScriptSandboxException"/>; cross-site routing is unavailable in a Test Run.
/// </summary>
/// <param name="attributeName">Attribute name (included in the exception message).</param>
/// <param name="targetValue">Unused target value.</param>
/// <param name="timeout">Unused timeout.</param>
/// <param name="cancellationToken">Unused token.</param>
/// <returns>Never returns; always throws <see cref="ScriptSandboxException"/>.</returns>
public Task<bool> WaitForAttribute(
string attributeName,
object? targetValue,
TimeSpan timeout,
CancellationToken cancellationToken = default) =>
throw Unavailable($"WaitForAttribute(\"{attributeName}\")");
private ScriptSandboxException Unavailable(string operation) => private ScriptSandboxException Unavailable(string operation) =>
new($"Route.To(\"{_instanceCode}\").{operation} is not available in Test Run — " + new($"Route.To(\"{_instanceCode}\").{operation} is not available in Test Run — " +
"cross-site routing needs a deployed site reachable over the cluster transport."); "cross-site routing needs a deployed site reachable over the cluster transport.");
@@ -644,4 +644,20 @@ public class ScriptAnalysisServiceTests
Assert.All(lines, l => Assert.Equal($"S{i}", l.Trim())); 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"));
}
} }