feat(flow-debugger): carry site-side trace truncation on the route reply (ScadaBridge#54)

ScriptCallResult and RouteToCallResponse gain trailing Truncated/LogsTruncated,
filled from the site FlowTraceCollector after Complete(); RouteToCallResponseDto
fields 8/9 carry them (sitecommand regen). FlowDebugRunner ORs them into
FlowDebugRunResult on the script entry, and on the method entry through a new
optional truncation sink on RouteHelper.WithTraceSink (carried through every
With* clone). Previously a site run whose collector dropped rows came back as a
silently short list. Older sites send false.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
Joseph Doherty
2026-11-09 12:37:57 -05:00
parent 5310fd0e26
commit 6d5bb6189f
15 changed files with 484 additions and 155 deletions
@@ -130,6 +130,45 @@ public class RouteHelperTraceTests
Assert.Same(siteEvents[1], sunk[2]);
}
/// <summary>
/// Gitea #54: a routed reply carrying the site collector's truncation flags is forwarded to the
/// truncation sink, and the sink survives the helper's clone chain (WithDeadline et al.).
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task TruncationSink_ReceivesTheReplysSiteTruncationFlags_ThroughClones()
{
var child = Guid.NewGuid();
_router.RouteToCallAsync("SiteA", Arg.Any<RouteToCallRequest>(), Arg.Any<CancellationToken>())
.Returns(ci => new RouteToCallResponse(((RouteToCallRequest)ci[1]).CorrelationId, true, null, null,
DateTimeOffset.UtcNow, child, [SiteEvent(child)], Truncated: false, LogsTruncated: true));
var reports = new List<(bool, bool)>();
await Helper()
.WithTraceSink(_ => { }, (t, lt) => reports.Add((t, lt)))
.WithTrace(true)
.WithDeadline(CancellationToken.None)
.WithSiteScope(null)
.To("inst-1").Call("doWork");
Assert.Equal([(false, true)], reports);
}
/// <summary>Gitea #54 control: an untruncated reply does not call the truncation sink.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task TruncationSink_IsNotCalled_ForAnUntruncatedReply()
{
_router.RouteToCallAsync("SiteA", Arg.Any<RouteToCallRequest>(), Arg.Any<CancellationToken>())
.Returns(ci => new RouteToCallResponse(((RouteToCallRequest)ci[1]).CorrelationId, true, null, null,
DateTimeOffset.UtcNow));
var calls = 0;
await Helper().WithTraceSink(_ => { }, (_, _) => calls++).To("inst-1").Call("doWork");
Assert.Equal(0, calls);
}
/// <summary>Verifies that a routing failure writes a failed row and still throws.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]