Merge wt/step-logs-t10: Task 10
This commit is contained in:
@@ -61,6 +61,11 @@ public record FlowDebugReplayCommand(Guid ExecutionId);
|
|||||||
/// <param name="Truncated">True when the inline event list was capped (the audit rows remain complete).</param>
|
/// <param name="Truncated">True when the inline event list was capped (the audit rows remain complete).</param>
|
||||||
/// <param name="StartedAtUtc">When the run started.</param>
|
/// <param name="StartedAtUtc">When the run started.</param>
|
||||||
/// <param name="FinishedAtUtc">When the run finished (or the replay was served).</param>
|
/// <param name="FinishedAtUtc">When the run finished (or the replay was served).</param>
|
||||||
|
/// <param name="LogsTruncated">
|
||||||
|
/// Step-logs (D7, 2026-09-09): true when the inline <c>FlowTraceCollector</c> dropped one
|
||||||
|
/// or more low-priority <c>ScriptLog</c> rows to protect room for step rows — the run's log lines
|
||||||
|
/// are incomplete inline, but Replay (which reads the audit store, never truncated) is complete.
|
||||||
|
/// </param>
|
||||||
public record FlowDebugRunResult(
|
public record FlowDebugRunResult(
|
||||||
Guid RootExecutionId,
|
Guid RootExecutionId,
|
||||||
bool Completed,
|
bool Completed,
|
||||||
@@ -69,4 +74,5 @@ public record FlowDebugRunResult(
|
|||||||
IReadOnlyList<AuditEvent> Events,
|
IReadOnlyList<AuditEvent> Events,
|
||||||
bool Truncated,
|
bool Truncated,
|
||||||
DateTimeOffset StartedAtUtc,
|
DateTimeOffset StartedAtUtc,
|
||||||
DateTimeOffset FinishedAtUtc);
|
DateTimeOffset FinishedAtUtc,
|
||||||
|
bool LogsTruncated = false);
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ public sealed class FlowDebugRunner
|
|||||||
executionId: root,
|
executionId: root,
|
||||||
durationMs: (int)Math.Min(sw.ElapsedMilliseconds, int.MaxValue),
|
durationMs: (int)Math.Min(sw.ElapsedMilliseconds, int.MaxValue),
|
||||||
errorMessage: result.ErrorMessage,
|
errorMessage: result.ErrorMessage,
|
||||||
|
errorDetail: result.ErrorDetail,
|
||||||
requestSummary: requestJson,
|
requestSummary: requestJson,
|
||||||
responseSummary: result.ResultJson,
|
responseSummary: result.ResultJson,
|
||||||
extra: new ScriptTraceExtra { Source = SourceTag }.ToJson());
|
extra: new ScriptTraceExtra { Source = SourceTag }.ToJson());
|
||||||
@@ -212,7 +213,7 @@ public sealed class FlowDebugRunner
|
|||||||
|
|
||||||
return new FlowDebugRunResult(
|
return new FlowDebugRunResult(
|
||||||
root, result.Success, result.ErrorMessage, result.Success ? null : result.ErrorCode,
|
root, result.Success, result.ErrorMessage, result.Success ? null : result.ErrorCode,
|
||||||
ordered, Truncated: collector.Truncated, startedAt, finishedAt);
|
ordered, Truncated: collector.Truncated, startedAt, finishedAt, LogsTruncated: collector.LogsTruncated);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -397,7 +398,8 @@ public sealed class FlowDebugRunner
|
|||||||
return new FlowDebugRunResult(
|
return new FlowDebugRunResult(
|
||||||
root, response.Success, response.ErrorMessage,
|
root, response.Success, response.ErrorMessage,
|
||||||
response.Success ? null : faultCode ?? CallFailedCode,
|
response.Success ? null : faultCode ?? CallFailedCode,
|
||||||
ordered, Truncated: collector.Truncated, startedAt, DateTimeOffset.UtcNow);
|
ordered, Truncated: collector.Truncated, startedAt, DateTimeOffset.UtcNow,
|
||||||
|
LogsTruncated: collector.LogsTruncated);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -152,6 +152,30 @@ public class FlowDebugRunnerTests
|
|||||||
Assert.Same(siteRun, result.Events[2]);
|
Assert.Same(siteRun, result.Events[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RunMethod_ScriptThrows_InboundRowCarriesErrorDetail()
|
||||||
|
{
|
||||||
|
// Step-logs D3: InboundScriptExecutor turns an unhandled script exception into a
|
||||||
|
// SCRIPT_ERROR InboundScriptResult whose ErrorDetail is the exception's own ToString() —
|
||||||
|
// never surfaced to the HTTP caller, but the Flow Debugger's inbound node reads it off
|
||||||
|
// the written audit row.
|
||||||
|
var method = new ApiMethod("M", "x") { Id = 21, TimeoutSeconds = 5 };
|
||||||
|
_apiRepo.GetApiMethodByIdAsync(21, Arg.Any<CancellationToken>()).Returns(method);
|
||||||
|
_executor.RegisterHandler("M", _ => throw new InvalidOperationException("boom"));
|
||||||
|
|
||||||
|
var result = await Runner().RunMethodAsync(_services.BuildServiceProvider(),
|
||||||
|
new FlowDebugRunMethodCommand(21, null), Deployer, CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.False(result.Completed);
|
||||||
|
Assert.Equal("SCRIPT_ERROR", result.ErrorCode);
|
||||||
|
var inbound = Assert.Single(_central, e => e.AsRow().Kind == AuditKind.InboundRequest).AsRow();
|
||||||
|
Assert.NotNull(inbound.ErrorDetail);
|
||||||
|
Assert.StartsWith("System.InvalidOperationException: boom", inbound.ErrorDetail);
|
||||||
|
// The returned copy carries the same detail (redacted, same as every other field).
|
||||||
|
var returnedInbound = result.Events[0].AsRow();
|
||||||
|
Assert.StartsWith("System.InvalidOperationException: boom", returnedInbound.ErrorDetail);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task RunMethod_UnknownMethod_IsNotFound()
|
public async Task RunMethod_UnknownMethod_IsNotFound()
|
||||||
{
|
{
|
||||||
@@ -389,6 +413,12 @@ public class FlowDebugRunnerTests
|
|||||||
executionId: Guid.NewGuid(), target: "work") with
|
executionId: Guid.NewGuid(), target: "work") with
|
||||||
{ DetailsJson = detailsJson };
|
{ DetailsJson = detailsJson };
|
||||||
|
|
||||||
|
/// <summary>A <c>ScriptLog</c> row — the low-priority lane <see cref="FlowStepProjector.IsLogLine"/> recognizes.</summary>
|
||||||
|
private static AuditEvent LogRow() =>
|
||||||
|
ScadaBridgeAuditEventFactory.Create(
|
||||||
|
AuditChannel.ScriptTrace, AuditKind.ScriptLog, AuditStatus.Delivered,
|
||||||
|
executionId: Guid.NewGuid(), target: "work");
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task RunMethod_SinkEvents_AreRedacted_WhenARedactorIsRegistered()
|
public async Task RunMethod_SinkEvents_AreRedacted_WhenARedactorIsRegistered()
|
||||||
{
|
{
|
||||||
@@ -454,6 +484,37 @@ public class FlowDebugRunnerTests
|
|||||||
|
|
||||||
Assert.True(result.Truncated);
|
Assert.True(result.Truncated);
|
||||||
Assert.Equal(501, result.Events.Count); // inbound row + the 500 kept
|
Assert.Equal(501, result.Events.Count); // inbound row + the 500 kept
|
||||||
|
Assert.False(result.LogsTruncated); // these are full-priority step rows, not log lines
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RunMethod_NoLogRows_LogsTruncatedIsFalse()
|
||||||
|
{
|
||||||
|
WireSinkPushingMethod(33, [TraceRow("{}")]);
|
||||||
|
|
||||||
|
var result = await Runner().RunMethodAsync(_services.BuildServiceProvider(),
|
||||||
|
new FlowDebugRunMethodCommand(33, null), Deployer, CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.True(result.Completed, result.Error);
|
||||||
|
Assert.False(result.LogsTruncated);
|
||||||
|
Assert.False(result.Truncated);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task RunMethod_OverLowPriorityReserve_ReportsLogsTruncated_ButNotTruncated()
|
||||||
|
{
|
||||||
|
// FlowTraceCollector reserves the last LowPriorityReserveEvents (50) of DefaultMaxEvents
|
||||||
|
// (500) for full-priority step rows — a low-priority (ScriptLog) Add stops at 450 and
|
||||||
|
// sets LogsTruncated, leaving Truncated (the step-row fidelity signal) untouched.
|
||||||
|
WireSinkPushingMethod(34, Enumerable.Range(0, 451).Select(_ => LogRow()).ToList());
|
||||||
|
|
||||||
|
var result = await Runner().RunMethodAsync(_services.BuildServiceProvider(),
|
||||||
|
new FlowDebugRunMethodCommand(34, null), Deployer, CancellationToken.None);
|
||||||
|
|
||||||
|
Assert.True(result.Completed, result.Error);
|
||||||
|
Assert.True(result.LogsTruncated);
|
||||||
|
Assert.False(result.Truncated);
|
||||||
|
Assert.Equal(451, result.Events.Count); // inbound row + the 450 log rows kept
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Flow Debugger script entry (design 2026-09-08) ---
|
// --- Flow Debugger script entry (design 2026-09-08) ---
|
||||||
|
|||||||
Reference in New Issue
Block a user