fix(scriptanalysis): warn on scripts blocking on a Task (.Result/.Wait()/GetAwaiter().GetResult()) (review 2026-10-21 01/SR-U2)

Advisory, not a trust-policy violation: every policy entry blocks deploy, and
an error here would fail the next redeploy of already-deployed scripts.
ValidationService surfaces it as a non-blocking ScriptCompilation warning.
This commit is contained in:
Joseph Doherty
2026-10-22 06:22:01 -04:00
parent 2ca60f86ff
commit 5919a71b8b
8 changed files with 270 additions and 17 deletions
@@ -504,6 +504,44 @@ public class ValidationServiceTests
Assert.Contains(gated.Errors, e => e.Message.Contains("Ghost"));
}
/// <summary>
/// Review 2026-10-21 01/SR-U2: a script that blocks on a Task gets a non-blocking
/// warning (it still deploys — existing scripts are not broken), naming the script.
/// </summary>
[Fact]
public void Validate_ScriptBlockingOnTask_ReturnsWarningNotError()
{
var config = new FlattenedConfiguration
{
InstanceUniqueName = "Instance1",
Scripts = [new ResolvedScript { CanonicalName = "Blocker", Code = "var a = Alarms.CurrentAsync().Result;" }]
};
var result = _sut.Validate(config);
Assert.True(result.IsValid);
Assert.Contains(result.Warnings, w =>
w.Category == ValidationCategory.ScriptCompilation
&& w.EntityName == "Blocker"
&& w.Message.Contains(".Result"));
}
/// <summary>Verifies that an awaiting script produces no blocking-wait warning.</summary>
[Fact]
public void Validate_ScriptAwaitingTask_NoBlockingWaitWarning()
{
var config = new FlattenedConfiguration
{
InstanceUniqueName = "Instance1",
Scripts = [new ResolvedScript { CanonicalName = "Good", Code = "var a = await Alarms.CurrentAsync();" }]
};
var result = _sut.Validate(config);
Assert.True(result.IsValid);
Assert.DoesNotContain(result.Warnings, w => w.EntityName == "Good");
}
private static FlattenedConfiguration ConfigWithExpressionTriggerScript(string expression)
{
var json = System.Text.Json.JsonSerializer.Serialize(new { expression });