fix(flow-debugger): final-review remediation — 2000-row trace pull, shared ExceptionHeader parser, surrogate-safe clip, wording, alarm source, register renumber (I1/I2/M1-M8)

This commit is contained in:
Joseph Doherty
2026-09-09 13:30:48 -04:00
parent 0bacc3630e
commit 97f01ae98e
21 changed files with 462 additions and 71 deletions
@@ -0,0 +1,60 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using ZB.MOM.WW.Audit;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Audit;
namespace ZB.MOM.WW.ScadaBridge.InboundAPI.Tests;
/// <summary>
/// Flow Debugger step logs (design 2026-09-09 D7): the central <c>Log</c> global's per-message cap.
/// Final review M2 — the site runtime's <c>ScriptLogger.Clip</c> already backed off a char when the
/// cap landed between the halves of a surrogate pair; this side did not, so an emoji straddling the
/// boundary left a LONE surrogate on the row (which breaks JSON serialization further down the pipe).
/// </summary>
public class InboundScriptLoggerTests
{
private static (InboundScriptLogger Logger, List<AuditEvent> Rows) MakeTraced(int maxLines = 200)
{
var rows = new List<AuditEvent>();
var logger = new InboundScriptLogger(
NullLogger.Instance, rows.Add, maxLines, "DelmiaRecipeDownload", Guid.NewGuid(), "central-a");
return (logger, rows);
}
[Fact]
public void ASurrogatePairStraddlingTheCap_IsDroppedWhole()
{
var (logger, rows) = MakeTraced();
var straddling = new string('x', InboundScriptLogger.MaxMessageChars - 1) + "🚀" + new string('y', 10);
logger.Info(straddling);
var message = Assert.Single(rows).AsRow().RequestSummary!;
Assert.Equal(InboundScriptLogger.MaxMessageChars, message.Length);
Assert.EndsWith("…", message);
Assert.DoesNotContain(message, static c => char.IsSurrogate(c));
}
[Fact]
public void AnOverlongMessageWithNoSurrogateAtTheBoundary_KeepsTheFullCap()
{
var (logger, rows) = MakeTraced();
logger.Info(new string('x', InboundScriptLogger.MaxMessageChars + 500));
var message = Assert.Single(rows).AsRow().RequestSummary!;
Assert.Equal(InboundScriptLogger.MaxMessageChars + 1, message.Length); // cap + the ellipsis
Assert.Equal(new string('x', InboundScriptLogger.MaxMessageChars) + "…", message);
}
[Fact]
public void AMessageExactlyAtTheCap_PassesThroughUnclipped()
{
var (logger, rows) = MakeTraced();
var exact = new string('x', InboundScriptLogger.MaxMessageChars);
logger.Info(exact);
Assert.Equal(exact, Assert.Single(rows).AsRow().RequestSummary);
}
}