feat(mtconnect): make an MTConnect driver creatable + configurable in /raw (Task 18 Part B)
An MTConnect driver instance could not be authored at all: RawDriverTypeDialog
(the only DriverInstance creation surface) offered a hardcoded 9-entry list
without it, and DriverConfigModal's default: branch renders a warning with no
raw-JSON fallback — so even a hand-inserted row could not be configured and
DriverConfig stayed "{}", on which ParseOptions throws "missing required
AgentUri".
Adds the typed MTConnectDriverForm (the consistent sibling pattern) covering
agentUri / deviceName / the four polling knobs / probe / reconnect, wires the
dispatch case, and offers the type in the dialog.
- All decisions live in the static MTConnectDriverForm.BuildConfigJson +
MTConnectFormModel (plain C#), because this project has no bUnit.
- The wire shape mirrors the driver's private MTConnectDriverConfigDto, NOT
MTConnectDriverOptions: the latter's TimeSpan probe knobs would serialise as
"00:00:05" and never bind to the driver's integer intervalMs.
- arch-review 01/S-6: a non-positive timing knob is REMOVED from the document
rather than written, so ParseOptions substitutes the driver's own positive
default instead of the driver faulting at Initialize on RequirePositive.
Removal (not just skipping) also stops a stale positive value surviving and
disagreeing with what the form shows.
- Unknown top-level keys survive a load/save, so the driver's hand-authored
tags[] surface is not silently deleted by opening the form.
- _jsonOpts carries JsonStringEnumConverter, satisfying the
DriverPageJsonConverterTests fleet guard properly rather than by exemption.
New RawDriverTypeDialogCoverageTests resolves the offered set through
DriverTypeNames.All, so the next registered driver fails here until the dialog
offers it.
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
using System.Reflection;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.AdminUI.Components.Shared.Raw;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.AdminUI.Tests.Uns;
|
||||
|
||||
/// <summary>
|
||||
/// Coverage guard for the <c>/raw</c> "New driver" dialog. <see cref="RawDriverTypeDialog"/> is the
|
||||
/// ONLY surface that creates a <c>DriverInstance</c>, and its offered types are a hand-authored array
|
||||
/// — so a driver whose factory is registered but whose entry was never added here is
|
||||
/// <b>un-creatable in the AdminUI</b> with nothing failing anywhere (the MTConnect gap Task 16 found).
|
||||
/// This test resolves through <see cref="DriverTypeNames.All"/>, so a newly registered driver type
|
||||
/// fails here until the dialog offers it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// There is no bUnit in this project, so the dialog's markup cannot be rendered. The offered set is
|
||||
/// read by reflection off the private static <c>Types</c> table the markup enumerates — the same
|
||||
/// substitute-for-a-component-test approach <c>DriverPageJsonConverterTests</c> uses.
|
||||
/// </remarks>
|
||||
public sealed class RawDriverTypeDialogCoverageTests
|
||||
{
|
||||
private static IReadOnlyList<string> OfferedDriverTypes()
|
||||
{
|
||||
var field = typeof(RawDriverTypeDialog)
|
||||
.GetField("Types", BindingFlags.NonPublic | BindingFlags.Static);
|
||||
field.ShouldNotBeNull("RawDriverTypeDialog must expose its offered driver types as a static 'Types' table.");
|
||||
|
||||
var table = (ValueTuple<string, string>[])field!.GetValue(null)!;
|
||||
return [.. table.Select(t => t.Item2)];
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(AllDriverTypes))]
|
||||
public void New_driver_dialog_offers_every_registered_driver_type(string driverType)
|
||||
=> OfferedDriverTypes().ShouldContain(
|
||||
driverType,
|
||||
$"RawDriverTypeDialog must offer DriverTypeNames.{driverType} — otherwise a driver of that " +
|
||||
"type can never be created in /raw, which is the only creation surface.");
|
||||
|
||||
/// <summary>xUnit theory source over every registered driver-type constant.</summary>
|
||||
public static TheoryData<string> AllDriverTypes()
|
||||
{
|
||||
var data = new TheoryData<string>();
|
||||
foreach (var t in DriverTypeNames.All)
|
||||
data.Add(t);
|
||||
return data;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Offered_driver_types_are_canonical_constants_and_unique()
|
||||
{
|
||||
var offered = OfferedDriverTypes();
|
||||
offered.Distinct(StringComparer.Ordinal).Count().ShouldBe(offered.Count, "duplicate driver-type entries");
|
||||
foreach (var t in offered)
|
||||
DriverTypeNames.All.ShouldContain(t, $"'{t}' is not a canonical DriverTypeNames constant.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user