test(adminui): guard NativeAlarmModel driver set against IAlarmSource implementers (#642)
Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
This commit is contained in:
@@ -0,0 +1,53 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using Shouldly;
|
||||||
|
using Xunit;
|
||||||
|
using ZB.MOM.WW.OtOpcUa.AdminUI.Uns.TagEditors;
|
||||||
|
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||||
|
|
||||||
|
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gitea #642 guard: <see cref="NativeAlarmModel.SupportsNativeAlarms"/> is a hand-maintained set; it must equal
|
||||||
|
/// the drivers whose concrete types implement <see cref="IAlarmSource"/>. Discovery is by reflection over the
|
||||||
|
/// driver assemblies deployed beside this test; an assembly's driver type is the <see cref="DriverTypeNames"/>
|
||||||
|
/// constant whose FIELD NAME equals the assembly suffix (Driver.Galaxy -> Galaxy = "GalaxyMxGateway").
|
||||||
|
/// </summary>
|
||||||
|
public sealed class NativeAlarmDriverSetGuardTests
|
||||||
|
{
|
||||||
|
private static readonly string[] NonDriverSuffixes = [".Contracts", ".Addressing", ".Cli", ".Historian.Gateway"];
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SupportsNativeAlarms_matches_the_drivers_implementing_IAlarmSource()
|
||||||
|
{
|
||||||
|
var binDir = Path.GetDirectoryName(typeof(NativeAlarmDriverSetGuardTests).Assembly.Location)!;
|
||||||
|
var constants = typeof(DriverTypeNames).GetFields(BindingFlags.Public | BindingFlags.Static)
|
||||||
|
.Where(f => f.IsLiteral && f.FieldType == typeof(string))
|
||||||
|
.ToDictionary(f => f.Name, f => (string)f.GetRawConstantValue()!, StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
|
const string prefix = "ZB.MOM.WW.OtOpcUa.Driver.";
|
||||||
|
var implementers = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
var scanned = 0;
|
||||||
|
foreach (var dll in Directory.GetFiles(binDir, prefix + "*.dll"))
|
||||||
|
{
|
||||||
|
var name = Path.GetFileNameWithoutExtension(dll);
|
||||||
|
if (NonDriverSuffixes.Any(s => name.EndsWith(s, StringComparison.Ordinal))) { continue; }
|
||||||
|
Type[] types;
|
||||||
|
try { types = Assembly.LoadFrom(dll).GetTypes(); }
|
||||||
|
catch (ReflectionTypeLoadException ex) { types = ex.Types.Where(t => t is not null).ToArray()!; }
|
||||||
|
scanned++;
|
||||||
|
if (!types.Any(t => t.IsClass && !t.IsAbstract && typeof(IAlarmSource).IsAssignableFrom(t))) { continue; }
|
||||||
|
var suffix = name[prefix.Length..];
|
||||||
|
constants.TryGetValue(suffix, out var typeName).ShouldBeTrue($"no DriverTypeNames constant named '{suffix}' for {name}");
|
||||||
|
implementers.Add(typeName!);
|
||||||
|
}
|
||||||
|
|
||||||
|
scanned.ShouldBeGreaterThan(5, "driver assemblies were not deployed to the test output; the guard would be vacuous");
|
||||||
|
implementers.Count.ShouldBeGreaterThan(0);
|
||||||
|
|
||||||
|
foreach (var typeName in constants.Values)
|
||||||
|
{
|
||||||
|
NativeAlarmModel.SupportsNativeAlarms(typeName).ShouldBe(implementers.Contains(typeName),
|
||||||
|
$"NativeAlarmModel.AlarmDriverTypes disagrees with IAlarmSource for driver type '{typeName}'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,6 +40,10 @@
|
|||||||
<!-- #576: the AB form tests deserialise the saved JSON through the drivers' own factory parse. -->
|
<!-- #576: the AB form tests deserialise the saved JSON through the drivers' own factory parse. -->
|
||||||
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.AbCip\ZB.MOM.WW.OtOpcUa.Driver.AbCip.csproj"/>
|
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.AbCip\ZB.MOM.WW.OtOpcUa.Driver.AbCip.csproj"/>
|
||||||
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.AbLegacy\ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.csproj"/>
|
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.AbLegacy\ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.csproj"/>
|
||||||
|
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.MTConnect\ZB.MOM.WW.OtOpcUa.Driver.MTConnect.csproj"/>
|
||||||
|
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.FOCAS\ZB.MOM.WW.OtOpcUa.Driver.FOCAS.csproj"/>
|
||||||
|
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient\ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.csproj"/>
|
||||||
|
<ProjectReference Include="..\..\..\src\Drivers\ZB.MOM.WW.OtOpcUa.Driver.Galaxy\ZB.MOM.WW.OtOpcUa.Driver.Galaxy.csproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user