fix(cli): single-key note rows render as raw indented lines under their step (Task 14 review)

This commit is contained in:
Joseph Doherty
2026-09-09 12:15:05 -04:00
parent c6529fa996
commit c1b1702397
5 changed files with 159 additions and 17 deletions
@@ -1,6 +1,7 @@
using System.CommandLine;
using System.Text.Json;
using ZB.MOM.WW.Audit;
using ZB.MOM.WW.ScadaBridge.CLI;
using ZB.MOM.WW.ScadaBridge.CLI.Commands;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Audit;
@@ -14,6 +15,7 @@ namespace ZB.MOM.WW.ScadaBridge.CLI.Tests;
/// the command tree shape, <c>--params</c> parsing, and the table projection that turns a
/// <see cref="FlowDebugRunResult"/> into one row per step.
/// </summary>
[Collection("Console")]
public class FlowDebugCommandsTests
{
private static readonly Option<string> Url = new("--url") { Recursive = true };
@@ -378,4 +380,58 @@ public class FlowDebugCommandsTests
Assert.DoesNotContain("WARN", projected);
Assert.DoesNotContain("exception:", projected);
}
/// <summary>
/// Drives the real table renderer (<see cref="CommandHelpers.HandleResponse"/>'s table
/// branch, which is what every <c>flow-debug</c> command actually prints to the terminal)
/// rather than only inspecting the intermediate JSON — a code review of Task 14 found the
/// generic column-union renderer padded the log/exception <c>note</c> rows into a far-right
/// "note" column instead of printing them as raw indented lines under their step, which the
/// JSON-level assertions above could not have caught (Task 14 review, 2026-09-09).
/// </summary>
[Fact]
public void HandleResponse_TableFormat_ShowLogsTrue_PrintsLogLineRawUnderScriptRunRow_NoNoteHeader()
{
var json = StepLogsPayload(out var log1Time, out var log2Time);
var projected = FlowDebugTableProjection.ProjectEvents(json, showLogs: true);
var response = new ManagementResponse(200, projected, null, null);
var writer = new StringWriter();
Console.SetOut(writer);
try
{
var exitCode = CommandHelpers.HandleResponse(response, "table");
Assert.Equal(0, exitCode);
}
finally
{
Console.SetOut(new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true });
}
var lines = writer.ToString().Split('\n', StringSplitOptions.RemoveEmptyEntries);
// The header row must not gain a "note" column — a raw log/exception line has nowhere
// sensible to sit in a column table, so it must never become one.
Assert.DoesNotContain("note", lines[0]);
var scriptRunLineIndex = Array.FindIndex(lines, l => l.Contains("ScriptRun"));
Assert.True(scriptRunLineIndex >= 0, "expected a ScriptRun row in the rendered table");
var expectedLog1 = $" {log1Time:HH:mm:ss.fff} WARN script hello";
var expectedLog2 = $" {log2Time:HH:mm:ss.fff} WARN script world";
// The two log lines print as raw, unpadded lines immediately under the ScriptRun row —
// not aligned into any table column.
var log1Line = lines[scriptRunLineIndex + 1];
Assert.StartsWith(" ", log1Line);
Assert.Contains("WARN", log1Line);
Assert.Equal(expectedLog1, log1Line);
Assert.Equal(expectedLog2, lines[scriptRunLineIndex + 2]);
// The exception detail follows, also raw and capped at 3 of the 4 available lines.
Assert.Equal(" exception: System.InvalidOperationException: boom", lines[scriptRunLineIndex + 3]);
Assert.Equal(" at X", lines[scriptRunLineIndex + 4]);
Assert.Equal(" at Y", lines[scriptRunLineIndex + 5]);
Assert.DoesNotContain("at Z", writer.ToString());
}
}