feat(siteruntime): WaitForAsync/WaitResult + quality-gated WaitAsync (spec §3, §4.2)
This commit is contained in:
@@ -219,4 +219,80 @@ public class AttributeAccessorWaitAsyncTests : TestKit, IDisposable
|
||||
var req = probe.ExpectMsg<WaitForAttributeRequest>(TimeSpan.FromSeconds(5));
|
||||
Assert.Equal("Flag", req.AttributeName);
|
||||
}
|
||||
|
||||
// ── WaitForAsync (spec §3): scope resolution + populated WaitResult ───────
|
||||
|
||||
[Fact]
|
||||
public async Task WaitForAsync_Value_AppliesScopeResolution_AndSurfacesPopulatedWaitResult()
|
||||
{
|
||||
var probe = CreateTestProbe();
|
||||
var ctx = MakeContext(probe.Ref);
|
||||
|
||||
// Composed scope "TempSensor" — Resolve("Flag") => "TempSensor.Flag".
|
||||
var acc = new AttributeAccessor(ctx, "TempSensor");
|
||||
|
||||
var task = acc.WaitForAsync("Flag", true, TimeSpan.FromSeconds(30));
|
||||
|
||||
// The actor receives the scope-resolved, codec-encoded request.
|
||||
var req = probe.ExpectMsg<WaitForAttributeRequest>(TimeSpan.FromSeconds(5));
|
||||
Assert.Equal("TempSensor.Flag", req.AttributeName);
|
||||
Assert.Equal(AttributeValueCodec.Encode(true), req.TargetValueEncoded);
|
||||
Assert.Null(req.Predicate);
|
||||
Assert.False(req.RequireGoodQuality);
|
||||
|
||||
// Reply with a matched response — the accessor must surface the full WaitResult.
|
||||
probe.Reply(new WaitForAttributeResponse(
|
||||
req.CorrelationId, Matched: true, Value: true, Quality: "Good", TimedOut: false));
|
||||
|
||||
var result = await task;
|
||||
Assert.True(result.Matched);
|
||||
Assert.Equal(true, result.Value);
|
||||
Assert.Equal("Good", result.Quality);
|
||||
Assert.False(result.TimedOut);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WaitForAsync_Predicate_AppliesScopeResolution_AndSurfacesWaitResult()
|
||||
{
|
||||
var probe = CreateTestProbe();
|
||||
var ctx = MakeContext(probe.Ref);
|
||||
|
||||
var acc = new AttributeAccessor(ctx, "Motor.TempSensor");
|
||||
|
||||
Func<object?, bool> predicate = _ => true;
|
||||
var task = acc.WaitForAsync("Level", predicate, TimeSpan.FromSeconds(30));
|
||||
|
||||
var req = probe.ExpectMsg<WaitForAttributeRequest>(TimeSpan.FromSeconds(5));
|
||||
Assert.Equal("Motor.TempSensor.Level", req.AttributeName);
|
||||
Assert.Null(req.TargetValueEncoded);
|
||||
Assert.NotNull(req.Predicate);
|
||||
|
||||
probe.Reply(new WaitForAttributeResponse(
|
||||
req.CorrelationId, Matched: true, Value: 42, Quality: "Good", TimedOut: false));
|
||||
|
||||
var result = await task;
|
||||
Assert.True(result.Matched);
|
||||
Assert.Equal(42, result.Value);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WaitForAsync_RequireGoodQuality_ThreadsFlagIntoRequest()
|
||||
{
|
||||
var probe = CreateTestProbe();
|
||||
var ctx = MakeContext(probe.Ref);
|
||||
|
||||
var acc = new AttributeAccessor(ctx, "");
|
||||
var task = acc.WaitForAsync("Flag", true, TimeSpan.FromSeconds(30), requireGoodQuality: true);
|
||||
|
||||
var req = probe.ExpectMsg<WaitForAttributeRequest>(TimeSpan.FromSeconds(5));
|
||||
Assert.True(req.RequireGoodQuality);
|
||||
|
||||
probe.Reply(new WaitForAttributeResponse(
|
||||
req.CorrelationId, Matched: false, Value: null, Quality: null, TimedOut: true));
|
||||
|
||||
var result = await task;
|
||||
Assert.False(result.Matched);
|
||||
Assert.True(result.TimedOut);
|
||||
Assert.Null(result.Value);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user