Files
lmxopcua/tests/Server/ZB.MOM.WW.OtOpcUa.AdminUI.Tests/Uns/UnsTreeServiceStructureTests.cs
T
Joseph Doherty b211911f98
v2-ci / unit-tests (push) Failing after 13m22s
test: migrate AdminUI/ControlPlane/Runtime/Host tests to xunit.v3 (#756)
Akka.TestKit.Xunit2 -> Akka.TestKit.Xunit 1.5.62 (xunit.v3 TestKit; needs xunit.v3 3.2.2, set via
VersionOverride on the four projects). Removes the xunit 2.9.3 pin. IAsyncLifetime returns ValueTask,
SqlServerFactAttribute gains the source-info ctor, and xUnit1051 call sites pass
TestContext.Current.CancellationToken. Test counts unchanged.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
2026-11-10 08:57:33 -05:00

96 lines
3.7 KiB
C#

using Shouldly;
using Xunit;
using ZB.MOM.WW.OtOpcUa.AdminUI.Uns;
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;
/// <summary>
/// Verifies <see cref="UnsTreeService.LoadStructureAsync"/> builds the Enterprise→Cluster→
/// Area→Line→Equipment structure from the config database, including per-equipment tag/
/// virtual-tag counts and the retention of empty clusters.
/// </summary>
[Trait("Category", "Unit")]
public sealed class UnsTreeServiceStructureTests
{
private static UnsTreeService SeededService()
{
var dbName = $"uns-{Guid.NewGuid():N}";
UnsTreeTestDb.SeedNamed(dbName);
return new UnsTreeService(UnsTreeTestDb.Factory(dbName));
}
/// <summary>Root enterprise "zb" carries both clusters, and MAIN exposes the full
/// area→line→equipment path with the expected kinds and keys.</summary>
[Fact]
public async Task LoadStructure_builds_full_hierarchy()
{
var service = SeededService();
var roots = await service.LoadStructureAsync(TestContext.Current.CancellationToken);
var enterprise = roots.ShouldHaveSingleItem();
enterprise.Kind.ShouldBe(UnsNodeKind.Enterprise);
enterprise.DisplayName.ShouldBe("zb");
enterprise.Children.Count.ShouldBe(2);
enterprise.Children.ShouldAllBe(c => c.Kind == UnsNodeKind.Cluster);
var main = enterprise.Children.Single(c => c.ClusterId == UnsTreeTestDb.PopulatedClusterId);
main.Kind.ShouldBe(UnsNodeKind.Cluster);
var area = main.Children.ShouldHaveSingleItem();
area.Kind.ShouldBe(UnsNodeKind.Area);
area.Key.ShouldBe("area:AREA-1");
area.EntityId.ShouldBe("AREA-1");
var line = area.Children.ShouldHaveSingleItem();
line.Kind.ShouldBe(UnsNodeKind.Line);
line.Key.ShouldBe("line:LINE-1");
line.EntityId.ShouldBe("LINE-1");
var equipment = line.Children.ShouldHaveSingleItem();
equipment.Kind.ShouldBe(UnsNodeKind.Equipment);
equipment.Key.ShouldBe($"eq:{UnsTreeTestDb.SeededEquipmentId}");
equipment.EntityId.ShouldBe(UnsTreeTestDb.SeededEquipmentId);
equipment.ClusterId.ShouldBe(UnsTreeTestDb.PopulatedClusterId);
}
/// <summary>The seeded equipment node's badge count equals tags + virtual tags. Equipment is a
/// tree leaf (no expander), so HasLazyChildren is always false.</summary>
[Fact]
public async Task LoadStructure_counts_tags_and_vtags_per_equipment()
{
var service = SeededService();
var roots = await service.LoadStructureAsync(TestContext.Current.CancellationToken);
var equipment = roots
.Single()
.Children.Single(c => c.ClusterId == UnsTreeTestDb.PopulatedClusterId)
.Children.Single()
.Children.Single()
.Children.Single();
// v3: Tag is Raw-only — driver tags no longer bind to equipment, so only the 1 virtual tag
// contributes to the per-equipment badge count (the 2 seeded raw tags are not counted).
equipment.ChildCount.ShouldBe(1);
equipment.HasLazyChildren.ShouldBeFalse();
}
/// <summary>An empty cluster (no areas) is still rendered as a Cluster node with no children.</summary>
[Fact]
public async Task LoadStructure_includes_empty_clusters()
{
var service = SeededService();
var roots = await service.LoadStructureAsync(TestContext.Current.CancellationToken);
var siteA = roots
.Single()
.Children.Single(c => c.ClusterId == UnsTreeTestDb.EmptyClusterId);
siteA.Kind.ShouldBe(UnsNodeKind.Cluster);
siteA.Children.ShouldBeEmpty();
siteA.ChildCount.ShouldBe(0);
}
}