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
67 lines
2.5 KiB
C#
67 lines
2.5 KiB
C#
using CliFx;
|
|
using CliFx.Attributes;
|
|
using CliFx.Infrastructure;
|
|
using Shouldly;
|
|
using Xunit;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.AbCip.Cli.Tests;
|
|
|
|
/// <summary>The CLI <c>--transport</c> flag reaches <see cref="AbCipDriverOptions.Transport"/>.</summary>
|
|
[Trait("Category", "Unit")]
|
|
public sealed class AbCipTransportOptionTests
|
|
{
|
|
[Command("capture-transport")]
|
|
private sealed class CapturingCommand : AbCipCommandBase
|
|
{
|
|
public static AbCipTransport? Captured;
|
|
public static bool Ran;
|
|
|
|
public override ValueTask ExecuteAsync(IConsole console)
|
|
{
|
|
Ran = true;
|
|
Captured = BuildOptions([]).Transport;
|
|
return default;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// With no flag the CLI leaves the option null ("use the default"), and the driver's resolver makes
|
|
/// that Native — the same path an absent <c>transport</c> key takes in a deployed config.
|
|
/// </summary>
|
|
[Fact]
|
|
public async Task Transport_defaults_to_the_resolver_default_native()
|
|
{
|
|
CapturingCommand.Captured = null;
|
|
CapturingCommand.Ran = false;
|
|
using var console = new FakeInMemoryConsole();
|
|
var app = new CliApplicationBuilder().AddCommand(typeof(CapturingCommand)).UseConsole(console).Build();
|
|
|
|
var exit = await app.RunAsync(["capture-transport", "--gateway", "ab://1.2.3.4/1,0"]);
|
|
|
|
exit.ShouldBe(0, console.ReadErrorString());
|
|
CapturingCommand.Ran.ShouldBeTrue();
|
|
CapturingCommand.Captured.ShouldBeNull();
|
|
AbCipTransportResolver.Resolve(CapturingCommand.Captured).ShouldBe(AbCipTransport.Native);
|
|
}
|
|
|
|
/// <summary>Verifies CliFx parses <c>--transport</c> (case-insensitive) into the options.</summary>
|
|
[Theory]
|
|
[InlineData("native", AbCipTransport.Native)]
|
|
[InlineData("Native", AbCipTransport.Native)]
|
|
[InlineData("libplctag", AbCipTransport.Libplctag)]
|
|
[InlineData("default", null)]
|
|
public async Task Transport_flag_is_parsed_by_the_cli(string value, AbCipTransport? expected)
|
|
{
|
|
CapturingCommand.Ran = false;
|
|
CapturingCommand.Captured = null;
|
|
using var console = new FakeInMemoryConsole();
|
|
var app = new CliApplicationBuilder().AddCommand(typeof(CapturingCommand)).UseConsole(console).Build();
|
|
|
|
var exit = await app.RunAsync(["capture-transport", "--gateway", "ab://1.2.3.4/1,0", "--transport", value]);
|
|
|
|
exit.ShouldBe(0, console.ReadErrorString());
|
|
CapturingCommand.Ran.ShouldBeTrue();
|
|
CapturingCommand.Captured.ShouldBe(expected);
|
|
}
|
|
}
|