bd6c0b4d3d
Add missing <returns>/<param>/<summary>/<typeparam> tags and clean up misused inheritdoc across 481 files so the documented API surface is complete. Documentation-only (zero code lines changed). The 131 remaining findings are inheritdoc-style warnings deliberately left to preserve hand-written implementation rationale (plan-decision notes, race-condition explanations).
72 lines
3.3 KiB
C#
72 lines
3.3 KiB
C#
using Microsoft.Extensions.DependencyInjection;
|
|
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
|
using ZB.MOM.WW.OtOpcUa.Host.Drivers;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;
|
|
|
|
/// <summary>
|
|
/// Guards the Test Connect wiring contract: every driver type editable in the AdminUI must have
|
|
/// a registered <see cref="IDriverProbe"/>, resolvable from the same DI container that hosts the
|
|
/// <c>admin-operations</c> cluster singleton. The singleton is role-pinned to <c>admin</c>, so on
|
|
/// a split-role deployment (the MAIN cluster's admin-only nodes) the probes must be wired by the
|
|
/// admin path — not only the driver path — or every Test Connect button returns
|
|
/// "No probe registered for driver type X".
|
|
/// </summary>
|
|
public sealed class DriverProbeRegistrationTests
|
|
{
|
|
// The canonical "all drivers" set — one entry per AdminUI typed driver page's DriverTypeKey.
|
|
// Keep in sync with the DriverTypeKey constants in
|
|
// src/Server/.../Components/Pages/Clusters/Drivers/*DriverPage.razor.
|
|
private static readonly string[] AdminUiDriverTypeKeys =
|
|
[
|
|
"ModbusTcp",
|
|
"AbCip",
|
|
"AbLegacy",
|
|
"S7",
|
|
"TwinCat", // page key; probe reports "TwinCAT" — must resolve case-insensitively
|
|
"Focas", // page key; probe reports "FOCAS" — must resolve case-insensitively
|
|
"OpcUaClient",
|
|
"GalaxyMxGateway",
|
|
"Historian.Wonderware",
|
|
];
|
|
|
|
/// <summary>Verifies that AddOtOpcUaDriverProbes registers a probe for every AdminUI driver type.</summary>
|
|
[Fact]
|
|
public void AddOtOpcUaDriverProbes_registers_a_probe_for_every_AdminUI_driver_type()
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddOtOpcUaDriverProbes();
|
|
|
|
using var sp = services.BuildServiceProvider();
|
|
var probes = sp.GetServices<IDriverProbe>().ToList();
|
|
|
|
// No duplicate DriverType — AdminOperationsActor builds a dictionary keyed by DriverType
|
|
// (case-insensitive) and would throw on a duplicate key, crashing the singleton.
|
|
var byType = probes.ToDictionary(p => p.DriverType, StringComparer.OrdinalIgnoreCase);
|
|
|
|
foreach (var key in AdminUiDriverTypeKeys)
|
|
byType.ContainsKey(key).ShouldBeTrue($"No IDriverProbe registered for AdminUI driver type '{key}'.");
|
|
}
|
|
|
|
/// <summary>Verifies that AddOtOpcUaDriverProbes is idempotent when called multiple times.</summary>
|
|
[Fact]
|
|
public void AddOtOpcUaDriverProbes_is_idempotent()
|
|
{
|
|
// A fused admin,driver node calls the registration from both the driver-factory path and the
|
|
// admin path. TryAddEnumerable must de-dup so the probe set stays unique (else the actor's
|
|
// ToDictionary throws).
|
|
var services = new ServiceCollection();
|
|
services.AddOtOpcUaDriverProbes();
|
|
services.AddOtOpcUaDriverProbes();
|
|
|
|
using var sp = services.BuildServiceProvider();
|
|
var probes = sp.GetServices<IDriverProbe>().ToList();
|
|
|
|
var distinctTypes = probes.Select(p => p.DriverType).Distinct(StringComparer.OrdinalIgnoreCase).Count();
|
|
probes.Count.ShouldBe(distinctTypes, "Duplicate IDriverProbe registrations — TryAddEnumerable should de-dup.");
|
|
distinctTypes.ShouldBe(AdminUiDriverTypeKeys.Length);
|
|
}
|
|
}
|