test(v3): migrate OpcUaServer.Tests + Host.IntegrationTests to v3 dark address space
Migrate the server composer/applier + 2-node harness tests off the retired
Namespace/NamespaceKind + equipment→driver/device binding schema onto the v3
greenfield shape (raw-only Tag; DriverInstance-RawFolderId; equipment via UnsLine).
OpcUaServer.Tests (134 build errors -> green, 335 pass / 4 skip):
- LIVE (migrated, passing): VirtualTag-historize + ScriptedAlarm composition,
composer purity/hierarchy, applier MaterialiseHierarchy + MaterialiseEquipmentTags
idempotency (hand-fed applier, not dark).
- SKIP dark-until-Batch-4: {{equip}} token expansion, applier equipment-namespace
E2E (equipment-tag variable materialization).
- RETIRE-with-comment / skip: alias-tag (Tag.EquipmentId binding gone) + device-host
equipment binding (architecturally removed).
- Add local DarkAddressSpaceReasons Skip-reason constants (mirrors Runtime.Tests).
Host.IntegrationTests (green, in-memory): drop deleted-Namespace seeds from
MultiClusterScoping + DriverReconnect; reshape EquipmentNamespaceMaterialization to
assert the DARK contract (folder nodes decode, EquipmentTags empty despite a seeded
v3 raw tag) with the full raw-tag->EquipmentTag materialization kept as a
Batch-4-pending Skip.
Tests-only; no src/ changes.
This commit is contained in:
+15
-86
@@ -1,98 +1,27 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Configuration.Entities;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Covers follow-up E projection: <see cref="EquipmentNode"/> carries the equipment's
|
||||
/// <c>DriverInstanceId</c> / <c>DeviceId</c> bindings and the resolved <c>DeviceHost</c> (parsed from
|
||||
/// the bound <see cref="Device"/>'s schemaless <c>DeviceConfig</c> JSON via the shared
|
||||
/// <c>DeviceConfigIntent.TryExtractHost</c>). A later task grafts a driver's discovered
|
||||
/// FixedTree onto a zero-tag equipment and partitions a multi-device driver by host using these.
|
||||
/// <para>The direct host-extraction + normalization unit tests moved to
|
||||
/// <c>Commons.Tests/DeviceConfigIntentTests</c> (R2-11) — this suite keeps the Compose-level projection
|
||||
/// coverage.</para>
|
||||
/// RETIRED (v3): this suite characterized follow-up E's <see cref="EquipmentNode"/> projection of an
|
||||
/// equipment's <c>DriverInstanceId</c> / <c>DeviceId</c> bindings + the resolved <c>DeviceHost</c>
|
||||
/// (parsed from the bound device's <c>DeviceConfig</c>). In v3 the equipment↔device binding is
|
||||
/// architecturally removed — <c>Equipment</c> no longer carries <c>DriverInstanceId</c>/<c>DeviceId</c>,
|
||||
/// <c>AddressSpaceComposer.Compose</c> no longer takes a <c>devices</c> list, and equipment references
|
||||
/// raw tags via <c>UnsTagReference</c> instead of hosting a device. <c>EquipmentNode</c>'s
|
||||
/// driver/device/host fields are therefore always null.
|
||||
/// <para>The coverage did not vanish, it moved: the device-endpoint merge is exercised by
|
||||
/// <c>Runtime.Tests.Drivers.DeploymentArtifactRawPathTests</c> (nested RawPath + device-endpoint merge),
|
||||
/// and the host-extraction/normalization unit lives in <c>Commons.Tests.DeviceConfigIntentTests</c>
|
||||
/// (R2-11). The Skipped placeholder below preserves the retirement rationale in test output.</para>
|
||||
/// </summary>
|
||||
public sealed class AddressSpaceComposerDeviceHostTests
|
||||
{
|
||||
/// <summary>An equipment bound to a driver + a device whose config carries a top-level
|
||||
/// <c>HostAddress</c> resolves all three fields, with the host trimmed + lower-cased.</summary>
|
||||
[Fact]
|
||||
[Fact(Skip = DarkAddressSpaceReasons.EquipmentDeviceBindingRetired)]
|
||||
public void Compose_resolves_driver_device_and_device_host()
|
||||
{
|
||||
var equipment = new[] { NewEquipment("eq-1", driver: "d1", device: "dev1") };
|
||||
var devices = new[] { NewDevice("dev1", "d1", "{\"HostAddress\":\"10.0.0.5:8193\"}") };
|
||||
|
||||
var node = AddressSpaceComposer.Compose(
|
||||
Array.Empty<UnsArea>(), Array.Empty<UnsLine>(), equipment,
|
||||
Array.Empty<DriverInstance>(), Array.Empty<ScriptedAlarm>(), devices: devices)
|
||||
.EquipmentNodes.ShouldHaveSingleItem();
|
||||
|
||||
node.EquipmentId.ShouldBe("eq-1");
|
||||
node.DriverInstanceId.ShouldBe("d1");
|
||||
node.DeviceId.ShouldBe("dev1");
|
||||
node.DeviceHost.ShouldBe("10.0.0.5:8193");
|
||||
// Retired: see class summary. Equipment no longer binds a driver/device; the EquipmentNode
|
||||
// driver/device/host projection is gone. Coverage moved to DeploymentArtifactRawPathTests
|
||||
// (device-endpoint merge) + DeviceConfigIntentTests (host extraction).
|
||||
}
|
||||
|
||||
/// <summary>An equipment with no driver and no device → all three new fields null (driver-less,
|
||||
/// no device to resolve a host from).</summary>
|
||||
[Fact]
|
||||
public void Compose_equipment_without_driver_or_device_yields_null_bindings()
|
||||
{
|
||||
var equipment = new[] { NewEquipment("eq-1", driver: null, device: null) };
|
||||
|
||||
var node = AddressSpaceComposer.Compose(
|
||||
Array.Empty<UnsArea>(), Array.Empty<UnsLine>(), equipment,
|
||||
Array.Empty<DriverInstance>(), Array.Empty<ScriptedAlarm>(), devices: Array.Empty<Device>())
|
||||
.EquipmentNodes.ShouldHaveSingleItem();
|
||||
|
||||
node.DriverInstanceId.ShouldBeNull();
|
||||
node.DeviceId.ShouldBeNull();
|
||||
node.DeviceHost.ShouldBeNull();
|
||||
}
|
||||
|
||||
/// <summary>A bound DeviceId with no matching device row, or a device whose config has no
|
||||
/// <c>HostAddress</c>, resolves DeviceHost to null while DeviceId is still carried.</summary>
|
||||
[Fact]
|
||||
public void Compose_device_host_is_null_when_unresolvable()
|
||||
{
|
||||
var equipment = new[]
|
||||
{
|
||||
NewEquipment("eq-missing", driver: "d1", device: "dev-missing"),
|
||||
NewEquipment("eq-nohost", driver: "d1", device: "dev-nohost"),
|
||||
};
|
||||
var devices = new[] { NewDevice("dev-nohost", "d1", "{\"Port\":502}") };
|
||||
|
||||
var nodes = AddressSpaceComposer.Compose(
|
||||
Array.Empty<UnsArea>(), Array.Empty<UnsLine>(), equipment,
|
||||
Array.Empty<DriverInstance>(), Array.Empty<ScriptedAlarm>(), devices: devices)
|
||||
.EquipmentNodes;
|
||||
|
||||
var missing = nodes.Single(n => n.EquipmentId == "eq-missing");
|
||||
missing.DeviceId.ShouldBe("dev-missing");
|
||||
missing.DeviceHost.ShouldBeNull();
|
||||
|
||||
var noHost = nodes.Single(n => n.EquipmentId == "eq-nohost");
|
||||
noHost.DeviceId.ShouldBe("dev-nohost");
|
||||
noHost.DeviceHost.ShouldBeNull();
|
||||
}
|
||||
|
||||
private static Equipment NewEquipment(string id, string? driver, string? device) => new()
|
||||
{
|
||||
EquipmentId = id,
|
||||
DriverInstanceId = driver,
|
||||
DeviceId = device,
|
||||
UnsLineId = "line-1",
|
||||
Name = id,
|
||||
MachineCode = id.ToUpperInvariant(),
|
||||
};
|
||||
|
||||
private static Device NewDevice(string deviceId, string driverInstanceId, string deviceConfig) => new()
|
||||
{
|
||||
DeviceId = deviceId,
|
||||
DriverInstanceId = driverInstanceId,
|
||||
Name = deviceId,
|
||||
DeviceConfig = deviceConfig,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user