Files
lmxopcua/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/HostsPanelWiringTests.cs
T
Joseph Doherty d33e4e20b5 feat(adminui): #546 wire DriverStatusPanel into /hosts driver rows (collapsed expander)
DriverStatusPanel was orphaned — zero references repo-wide — so the Restart and
Reconnect driver commands had NO reachable UI at all, despite working end to end
over the new gRPC mesh transport. Nothing failed to build, test or deploy while
that was true, which is why it went unnoticed.

The panel is now reachable per driver row on /hosts, behind a per-row "Controls"
expander that is collapsed by default: the table stays scannable, and only the
opened panels hold snapshot-store subscriptions. Expansion state is a HashSet
keyed by DriverInstanceId held on the page, so it survives the 5 s refresh that
replaces every row record. Both rows are @key'd by instance id so the panel —
which binds its DriverInstanceId once in OnInitializedAsync — can never be diffed
onto a different driver.

The panel's Enabled parameter is fed from real config rather than defaulted:
HostsDriverInstanceInfo/HostsDriverRow now carry DriverInstance.Enabled, which
LoadConfigAsync already had in hand. A snapshot with no matching configured
instance is treated as enabled — the row exists only because live health arrived,
so "Disabled — not deployed" would contradict the same row.

The expander is visible to every AuthenticatedRead viewer; the panel self-gates
only its Reconnect/Restart buttons on DriverOperator.

A source-scan pin (HostsPanelWiringTests) asserts /hosts still renders the panel
and still binds it to the row's own driver, cluster and enabled state — removing
the tag would silently restore the orphaned state.

Claude-Session: https://claude.ai/code/session_014WNM4vjoVksyyBraTXSZE1
2026-08-06 17:18:37 -04:00

60 lines
2.6 KiB
C#

using Shouldly;
using Xunit;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests;
/// <summary>
/// Pins <c>DriverStatusPanel</c> to the <c>/hosts</c> page (Gitea #546). The panel is the only
/// component carrying the Restart/Reconnect driver controls, and it had ZERO references
/// repo-wide — the commands worked end to end over the mesh transport while no user could reach
/// them, and nothing failed to build, test or deploy. Deleting the last <c>&lt;DriverStatusPanel</c>
/// tag would silently restore exactly that state, so it is pinned here.
/// </summary>
/// <remarks>
/// This is a source scan rather than a rendering assertion because the repo has no bUnit (same
/// technique as <c>MeshRegistrationRoleScopingTests</c>; panel markup is otherwise live-verified).
/// A scan proves reachability of the component, not that it renders correctly.
/// </remarks>
[Trait("Category", "Unit")]
public sealed class HostsPanelWiringTests
{
private static string ReadHostsPageSource()
{
var dir = new DirectoryInfo(AppContext.BaseDirectory);
while (dir is not null && !File.Exists(Path.Combine(dir.FullName, "ZB.MOM.WW.OtOpcUa.slnx")))
dir = dir.Parent;
dir.ShouldNotBeNull("could not locate the repository root (no ZB.MOM.WW.OtOpcUa.slnx above the test binary)");
return File.ReadAllText(Path.Combine(
dir!.FullName, "src", "Server", "ZB.MOM.WW.OtOpcUa.AdminUI",
"Components", "Pages", "Hosts.razor"));
}
/// <summary>THE #546 regression pin: the panel must remain reachable from a routable page.</summary>
[Fact]
public void Hosts_page_renders_the_driver_status_panel()
{
ReadHostsPageSource().ShouldContain(
"<DriverStatusPanel",
Case.Sensitive,
customMessage:
"/hosts no longer renders DriverStatusPanel — the Restart/Reconnect driver controls " +
"are orphaned again with no reachable UI, which is Gitea #546");
}
/// <summary>
/// The panel must be fed the row's real identity and enabled-state, not literals. Hard-coding
/// any of the three would make every expanded row drive the same driver, or claim a disabled
/// instance is live — both of which a source scan for the tag alone would pass.
/// </summary>
[Fact]
public void The_panel_is_bound_to_the_rows_own_driver_cluster_and_enabled_state()
{
var source = ReadHostsPageSource();
source.ShouldContain("DriverInstanceId=\"@d.DriverInstanceId\"");
source.ShouldContain("ClusterId=\"@g.ClusterId\"");
source.ShouldContain("Enabled=\"@d.Enabled\"");
}
}