Files
lmxopcua/tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests/RoleCaseVariantValidatorTests.cs
T

108 lines
4.8 KiB
C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using Shouldly;
using Xunit;
namespace ZB.MOM.WW.OtOpcUa.Cluster.Tests;
/// <summary>
/// R3-04 (01/C-9): a mixed-case role must fire the SAME fail-fast rule the canonical spelling
/// fires, on ALL FOUR role-scoped validators. Before the fix, three of the four
/// (<see cref="MeshTransportOptionsValidator"/>, <see cref="ConfigSourceOptionsValidator"/>,
/// <see cref="TelemetryOptionsValidator"/>) compared roles ordinally and silently PASSED a
/// misconfigured node spelled <c>Driver</c>/<c>Admin</c> — the load-bearing assertion in every
/// case below is the FAILURE, not the pass, because a validator whose driver/admin branch never
/// runs also "passes" vacuously.
/// </summary>
/// <remarks>
/// <see cref="SplitTopologyTransportValidator"/> already normalized before this fix (its own
/// comment names the hazard) — its case here is the BUILT-IN falsification control: it must have
/// been passing (i.e. correctly failing the misconfigured node) even before T4 landed, proving
/// the Theory shape itself is capable of catching a real regression.
/// </remarks>
[Collection(RoleEnvironmentCollection.Name)]
public sealed class RoleCaseVariantValidatorTests
{
public static readonly TheoryData<string> DriverSpellings = new()
{
"driver", "Driver", " driver ", "DRIVER",
};
private static IConfiguration Config(params (string Key, string Value)[] pairs) =>
new ConfigurationBuilder()
.AddInMemoryCollection(pairs.ToDictionary(p => p.Key, p => (string?)p.Value))
.Build();
// ---- SplitTopologyTransportValidator: the built-in control (already normalized pre-fix) -------
[Theory]
[MemberData(nameof(DriverSpellings))]
public void SplitTopology_fails_on_Dps_mesh_transport_for_every_driver_spelling(string driverSpelling)
{
var configuration = Config(
("Cluster:Roles:0", driverSpelling),
("Cluster:Roles:1", "cluster-SITE-A"),
("MeshTransport:Mode", MeshTransportOptions.ModeDps),
("Telemetry:Mode", TelemetryOptions.ModeGrpc));
var result = new SplitTopologyTransportValidator(configuration, EffectiveRoles.Resolve(configuration))
.Validate(MeshTransportOptions.SectionName, new MeshTransportOptions());
result.Failed.ShouldBeTrue();
result.FailureMessage.ShouldContain("MeshTransport:Mode");
}
// ---- MeshTransportOptionsValidator: GrpcPsk required for a driver node under Grpc mode --------
[Theory]
[MemberData(nameof(DriverSpellings))]
public void MeshTransport_Grpc_driver_node_without_a_psk_fails_for_every_driver_spelling(string driverSpelling)
{
var configuration = Config(("Cluster:Roles:0", driverSpelling), ("Cluster:Roles:1", "cluster-SITE-A"));
var options = new MeshTransportOptions
{
Mode = MeshTransportOptions.ModeGrpc,
GrpcPsk = null,
CentralGrpcEndpoints = ["http://central-1:4055"],
};
var result = new MeshTransportOptionsValidator(configuration, EffectiveRoles.Resolve(configuration))
.Validate(MeshTransportOptions.SectionName, options);
result.Failed.ShouldBeTrue();
result.FailureMessage.ShouldContain("MeshTransport:GrpcPsk");
}
// ---- ConfigSourceOptionsValidator: a driver-only node on Direct fails ---------------------------
[Theory]
[MemberData(nameof(DriverSpellings))]
public void ConfigSource_driver_only_node_on_Direct_fails_for_every_driver_spelling(string driverSpelling)
{
var configuration = Config(("Cluster:Roles:0", driverSpelling));
var options = new ConfigSourceOptions { Mode = ConfigSourceOptions.ModeDirect };
var result = new ConfigSourceOptionsValidator(EffectiveRoles.Resolve(configuration))
.Validate(ConfigSourceOptions.SectionName, options);
result.Failed.ShouldBeTrue();
result.FailureMessage.ShouldContain(ConfigSourceOptions.ModeFetchAndCache);
}
// ---- TelemetryOptionsValidator: a driver node under Grpc mode needs a listen port ---------------
[Theory]
[MemberData(nameof(DriverSpellings))]
public void Telemetry_Grpc_driver_node_with_no_listen_port_fails_for_every_driver_spelling(string driverSpelling)
{
var configuration = Config(("Cluster:Roles:0", driverSpelling));
var options = new TelemetryOptions { Mode = TelemetryOptions.ModeGrpc, ApiKey = "k" };
var result = new TelemetryOptionsValidator(EffectiveRoles.Resolve(configuration))
.Validate(TelemetryOptions.SectionName, options);
result.Failed.ShouldBeTrue();
result.FailureMessage.ShouldContain(nameof(TelemetryOptions.GrpcListenPort));
}
}