fix(inbound): type SITE_UNREACHABLE on AskTimeoutException at the router await instead of message-substring sniffing (plan R2-06 T5)
This commit is contained in:
@@ -98,19 +98,17 @@ public class InboundScriptExecutorTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RoutedSiteUnreachable_Returns500WithSiteUnreachableCode()
|
||||
public async Task RoutedAskTimeout_Returns500WithSiteUnreachableCode()
|
||||
{
|
||||
// C3: a routed call whose site is unreachable must surface the machine-readable
|
||||
// SITE_UNREACHABLE code (spec Component-InboundAPI failure body) rather than
|
||||
// collapsing into a generic SCRIPT_ERROR.
|
||||
// N3: unreachability is the Ask EXPIRING (site never answered) — typed as
|
||||
// AskTimeoutException by the communication layer — not a substring in a
|
||||
// failure message. Pre-fix this fell into the generic catch as SCRIPT_ERROR.
|
||||
var locator = Substitute.For<IInstanceLocator>();
|
||||
locator.GetSiteIdForInstanceAsync("X", Arg.Any<CancellationToken>()).Returns("SiteA");
|
||||
var router = Substitute.For<IInstanceRouter>();
|
||||
router.RouteToCallAsync("SiteA", Arg.Any<RouteToCallRequest>(), Arg.Any<CancellationToken>())
|
||||
.Returns(ci => new RouteToCallResponse(
|
||||
((RouteToCallRequest)ci[1]).CorrelationId,
|
||||
Success: false, ReturnValue: null, ErrorMessage: "Site unreachable",
|
||||
DateTimeOffset.UtcNow));
|
||||
.Returns(Task.FromException<RouteToCallResponse>(
|
||||
new Akka.Actor.AskTimeoutException("Timeout after 00:00:30")));
|
||||
var route = new RouteHelper(locator, router);
|
||||
|
||||
var method = new ApiMethod("routed", "return await Route.To(\"X\").Call(\"s\");")
|
||||
@@ -123,6 +121,33 @@ public class InboundScriptExecutorTests
|
||||
Assert.Equal("SITE_UNREACHABLE", result.ErrorCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SiteRespondedFailure_MessageMentioningUnreachable_IsScriptErrorNotSiteUnreachable()
|
||||
{
|
||||
// N3 anti-sniff: a site that RESPONDED with Success=false is reachable by
|
||||
// definition — even when its error text happens to contain "unreachable".
|
||||
// Pre-fix the substring sniff misclassified this as SITE_UNREACHABLE.
|
||||
var locator = Substitute.For<IInstanceLocator>();
|
||||
locator.GetSiteIdForInstanceAsync("X", Arg.Any<CancellationToken>()).Returns("SiteA");
|
||||
var router = Substitute.For<IInstanceRouter>();
|
||||
router.RouteToCallAsync("SiteA", Arg.Any<RouteToCallRequest>(), Arg.Any<CancellationToken>())
|
||||
.Returns(ci => new RouteToCallResponse(
|
||||
((RouteToCallRequest)ci[1]).CorrelationId,
|
||||
Success: false, ReturnValue: null,
|
||||
ErrorMessage: "PLC device unreachable behind the site gateway",
|
||||
DateTimeOffset.UtcNow));
|
||||
var route = new RouteHelper(locator, router);
|
||||
|
||||
var method = new ApiMethod("routed2", "return await Route.To(\"X\").Call(\"s\");")
|
||||
{ Id = 8, TimeoutSeconds = 10 };
|
||||
|
||||
var result = await _executor.ExecuteAsync(
|
||||
method, new Dictionary<string, object?>(), route, TimeSpan.FromSeconds(10));
|
||||
|
||||
Assert.False(result.Success);
|
||||
Assert.Equal("SCRIPT_ERROR", result.ErrorCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HandlerTimesOut_ReturnsTimeoutError()
|
||||
{
|
||||
|
||||
@@ -595,4 +595,30 @@ public class RouteHelperTests
|
||||
Assert.NotNull(captured);
|
||||
Assert.Equal(inboundExecutionId, captured!.ParentExecutionId);
|
||||
}
|
||||
|
||||
// --- N3: typed unreachability at the Ask boundary ---
|
||||
|
||||
[Fact]
|
||||
public async Task Call_AskTimeout_ThrowsSiteUnreachableException()
|
||||
{
|
||||
SiteResolves("inst", "SiteA");
|
||||
_router.RouteToCallAsync("SiteA", Arg.Any<RouteToCallRequest>(), Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromException<RouteToCallResponse>(
|
||||
new Akka.Actor.AskTimeoutException("Timeout after 00:00:30")));
|
||||
|
||||
await Assert.ThrowsAsync<SiteUnreachableException>(
|
||||
() => CreateHelper().To("inst").Call("s"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetAttributes_AskTimeout_ThrowsSiteUnreachableException()
|
||||
{
|
||||
SiteResolves("inst", "SiteA");
|
||||
_router.RouteToGetAttributesAsync("SiteA", Arg.Any<RouteToGetAttributesRequest>(), Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromException<RouteToGetAttributesResponse>(
|
||||
new Akka.Actor.AskTimeoutException("Timeout after 00:00:30")));
|
||||
|
||||
await Assert.ThrowsAsync<SiteUnreachableException>(
|
||||
() => CreateHelper().To("inst").GetAttributes(new[] { "a" }));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user