using Bunit;
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Deployment;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Debugging;
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Tests.Deployment;
///
/// ScadaBridge#56: the Flow Debugger's node-log section lists the lines other actors wrote for each
/// traced execution — grouped per execution under that execution's run step, with level filtering and
/// the logger category — and renders nothing when the graph has no node lines.
///
public class FlowNodeLogPanelTests : BunitContext
{
private static readonly DateTimeOffset T0 = new(2026, 11, 10, 12, 0, 0, TimeSpan.Zero);
private static FlowStep Run(Guid execution, string title)
=> new(Guid.NewGuid(), execution, null, FlowStepKind.ScriptRun, FlowLane.Site, title, null, T0, 10,
FlowStepOutcome.Ok, null, null, null, "site-a", "node-a", "M1", title, null);
private static FlowLogLine Node(Guid execution, string level, string message, int ms,
string category = "ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Actors.DataConnectionActor",
string? exceptionMessage = null)
=> new(Guid.NewGuid(), execution, T0.AddMilliseconds(ms), level, FlowLogLine.NodeSource, message,
exceptionMessage, exceptionMessage is null ? null : "System.Exception: " + exceptionMessage, null, ms, category);
private static FlowGraph Graph(IReadOnlyList steps, IReadOnlyList? nodeLogs)
=> new(steps, [], Predicted: false, Truncated: false, NodeLogs: nodeLogs);
/// No node lines (or no graph): the section is not rendered at all.
[Fact]
public void NoNodeLines_RendersNothing()
{
var execution = Guid.NewGuid();
Assert.Empty(Render(p => p.Add(x => x.Graph, Graph([Run(execution, "Runner")], null)))
.FindAll("[data-test=flow-nodelog-panel]"));
Assert.Empty(Render().FindAll("[data-test=flow-nodelog-panel]"));
}
/// Lines are grouped per execution, titled by its run step, with short category, level and counts.
[Fact]
public void Lines_AreGroupedPerExecution_UnderTheRunStep()
{
var a = Guid.NewGuid();
var b = Guid.NewGuid();
var graph = Graph([Run(a, "OnRecipe"), Run(b, "SharedHelper")],
[
Node(a, "debug", "Writing to tag T1", 1),
Node(b, "warn", "tag T2 bad quality", 2, category: "ZB.MOM.WW.ScadaBridge.SiteRuntime.Actors.InstanceActor"),
Node(a, "error", "write failed", 3, exceptionMessage: "timeout"),
]);
var cut = Render(p => p.Add(x => x.Graph, graph));
Assert.Equal("3 lines · 1 error · 1 warning · 1 info/debug", cut.Find("[data-test=flow-nodelog-count]").TextContent);
var groups = cut.FindAll("[data-test=flow-nodelog-execution]");
Assert.Equal(2, groups.Count);
Assert.Contains("OnRecipe", groups[0].TextContent);
Assert.Contains(a.ToString(), groups[0].TextContent);
var aRows = groups[0].QuerySelectorAll("tbody tr");
Assert.Equal(2, aRows.Length);
Assert.Contains("DataConnectionActor", aRows[0].TextContent);
Assert.DoesNotContain("ZB.MOM.WW", aRows[0].TextContent);
Assert.Contains("timeout", aRows[1].TextContent);
Assert.Contains("InstanceActor", groups[1].TextContent);
Assert.Contains("tag T2 bad quality", groups[1].TextContent);
}
/// Warn + hides debug/info lines; an execution left with none says so; All restores them.
[Fact]
public void WarnPlusFilter_HidesLowLevels()
{
var a = Guid.NewGuid();
var b = Guid.NewGuid();
var graph = Graph([Run(a, "OnRecipe"), Run(b, "Other")],
[Node(a, "info", "connected", 1), Node(a, "warn", "slow write", 2), Node(b, "debug", "chatter", 3)]);
var cut = Render(p => p.Add(x => x.Graph, graph));
cut.Find("[data-test=flow-nodelog-filter-warn]").Click();
var groups = cut.FindAll("[data-test=flow-nodelog-execution]");
Assert.Equal("warn", Assert.Single(groups[0].QuerySelectorAll("tbody tr")).GetAttribute("data-level"));
Assert.NotNull(groups[1].QuerySelector("[data-test=flow-nodelog-none-shown]"));
cut.Find("[data-test=flow-nodelog-filter-all]").Click();
Assert.Equal(3, cut.FindAll("[data-test=flow-nodelog-lines] tbody tr").Count);
}
/// Clicking the execution's title selects its run step.
[Fact]
public void ExecutionTitle_SelectsItsRunStep()
{
var a = Guid.NewGuid();
var run = Run(a, "OnRecipe");
FlowStep? selected = null;
var cut = Render(p => p
.Add(x => x.Graph, Graph([run], [Node(a, "warn", "slow", 1)]))
.Add(x => x.OnSelectStep, s => selected = s));
cut.Find("[data-test=flow-nodelog-step]").Click();
Assert.Same(run, selected);
}
}