ed0881d672
Task 22 (spec section 8 P5), with the controller rulings that exclude PLC-5 and LogixPccc. - Transport is nullable on both option records; null (key absent) means "use the default" and is never serialized. Factory DTO parse: absent -> null; a bad value still fails via ParseEnum. - One resolver per driver (AbCipTransportResolver / AbLegacyTransportResolver), used by the driver constructor, the Test Connect probe and (through the driver) the CLI. AbCip: null -> Native. AbLegacy: null -> Native unless any device is Plc5 or LogixPccc, then the whole driver resolves to Libplctag. Explicit values always win; explicit Native with a Plc5/LogixPccc device logs one Warning at init. - Init log shows configured vs effective, e.g. transport=Native (default). - AdminUI forms: "Default (...)" / "Native (managed)" / "libplctag (fallback)". Default omits the key on save (reverses Task 19's explicit re-emit, which would have pinned every edited driver). - CLIs: --transport default|native|libplctag, default "default" via the resolver, so --plc-type Plc5 with no --transport runs libplctag. - Tests pinned to Libplctag where a default-path runtime was created: found by instrumenting the native factories with the current xUnit test name (one unit test, AbLegacyReinitConfigAdoption; plus the two fixture-gated AbCip probe E2E tests whose exact messages were proven on libplctag). TransportMatrix and the differential harness already pass explicit transports. - Switch/resolver/CLI/form tests are falsified by mutating the resolver (default -> Libplctag, exclusion removed, AbLegacy default -> Libplctag): each mutation turns them red. - Replaces the inline role-name literal in AbCipDriverFactoryExtensions' ParseEnum call with a named constant; RoleAuthorityGuardTests was already red on the base commit for that line. - Docs: AbCip.md / AbLegacy.md default + release note; CLAUDE.md AB section. Claude-Session: https://claude.ai/code/session_01Ef2ozxS7QPanGT8uoATbTe
53 lines
2.7 KiB
C#
53 lines
2.7 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.AbLegacy.Tests;
|
|
|
|
/// <summary>
|
|
/// Gitea #516 — <c>AbLegacyDriver.InitializeAsync</c> used to serve the options its CONSTRUCTOR
|
|
/// captured and never look at the <c>driverConfigJson</c> it was handed, so an operator's config edit
|
|
/// was silently discarded while the deployment still sealed green.
|
|
/// <para>Every pre-existing reinit test in this suite passes <c>"{}"</c> — the exact input the guarded
|
|
/// re-parser treats as "keep the constructor options" — so they were blind to the defect.</para>
|
|
/// </summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class AbLegacyReinitConfigAdoptionTests
|
|
{
|
|
/// <summary>
|
|
/// The driver validates every device's <c>HostAddress</c> during init. Reinitializing with a config
|
|
/// whose device address is structurally invalid must therefore THROW — a driver still serving the
|
|
/// constructor's valid device list would validate that instead and succeed, which is precisely how
|
|
/// the discarded edit stayed invisible.
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task Reinitialize_adopts_a_changed_device_list()
|
|
{
|
|
var driver = AbLegacyDriverFactoryExtensions.CreateInstance(
|
|
"ab1",
|
|
"""{"devices":[{"hostAddress":"ab://10.0.0.1:44818/1,0","plcFamily":"Slc500"}]}""",
|
|
loggerFactory: null);
|
|
|
|
await Should.ThrowAsync<InvalidOperationException>(
|
|
() => driver.ReinitializeAsync(
|
|
"""{"devices":[{"hostAddress":"not-a-valid-ab-address","plcFamily":"Slc500"}]}""",
|
|
CancellationToken.None),
|
|
"AbLegacyDriver kept its constructor-supplied device list after a reinitialize with a changed config (#516)");
|
|
}
|
|
|
|
/// <summary>An empty/placeholder document must still keep the constructor options, so the many
|
|
/// lifecycle tests that pass <c>"{}"</c> keep meaning what they meant.</summary>
|
|
[Fact]
|
|
public async Task Reinitialize_with_an_empty_document_keeps_the_constructor_options()
|
|
{
|
|
// Pinned to libplctag (Task 22): this init starts the default probe loop against 10.0.0.1 with no
|
|
// injected factory, and was written and proven on libplctag. Pinning keeps it off the process-wide
|
|
// native DeviceSessionPool rather than silently changing what it exercises.
|
|
var driver = AbLegacyDriverFactoryExtensions.CreateInstance(
|
|
"ab1",
|
|
"""{"transport":"Libplctag","devices":[{"hostAddress":"ab://10.0.0.1:44818/1,0","plcFamily":"Slc500"}]}""",
|
|
loggerFactory: null);
|
|
|
|
await Should.NotThrowAsync(() => driver.ReinitializeAsync("{}", CancellationToken.None));
|
|
}
|
|
}
|