using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Options; using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.Cluster.Tests; /// /// A Grpc-mode node with no listen port or no key does not error — it simply never serves /// telemetry, and whoever is watching sees nothing with no stack trace to point at. These tests /// make that misconfiguration a loud host-start failure instead. /// public class TelemetryOptionsValidatorTests { private static ValidateOptionsResult Validate(TelemetryOptions o, params string[] roles) { var pairs = new Dictionary(); for (var i = 0; i < roles.Length; i++) { pairs[$"Cluster:Roles:{i}"] = roles[i]; } var configuration = new ConfigurationBuilder().AddInMemoryCollection(pairs).Build(); return new TelemetryOptionsValidator(configuration).Validate(TelemetryOptions.SectionName, o); } [Fact] public void Default_options_are_valid() { Validate(new TelemetryOptions()).Succeeded.ShouldBeTrue(); } [Fact] public void Unknown_mode_fails() { var result = Validate(new TelemetryOptions { Mode = "stream" }); result.Failed.ShouldBeTrue(); result.FailureMessage.ShouldContain("stream"); } [Fact] public void Mode_matching_is_case_insensitive() { Validate(new TelemetryOptions { Mode = "grpc", GrpcListenPort = 5100, ApiKey = "k", }, "driver").Succeeded.ShouldBeTrue(); } [Fact] public void Grpc_driver_node_with_no_listen_port_fails() { var result = Validate( new TelemetryOptions { Mode = TelemetryOptions.ModeGrpc, ApiKey = "k" }, "driver"); result.Failed.ShouldBeTrue(); result.FailureMessage.ShouldContain(nameof(TelemetryOptions.GrpcListenPort)); } [Fact] public void Grpc_with_empty_key_fails() { var result = Validate( new TelemetryOptions { Mode = TelemetryOptions.ModeGrpc, GrpcListenPort = 5100, ApiKey = "" }, "driver"); result.Failed.ShouldBeTrue(); result.FailureMessage.ShouldContain(nameof(TelemetryOptions.ApiKey)); } [Fact] public void Grpc_driver_with_port_and_key_is_valid() { Validate( new TelemetryOptions { Mode = TelemetryOptions.ModeGrpc, GrpcListenPort = 5100, ApiKey = "k" }, "driver").Succeeded.ShouldBeTrue(); } [Fact] public void Grpc_admin_only_node_does_not_require_a_listen_port() { // The listen-port requirement is driver-specific — an admin-only node dialling out (via // TelemetryDialOptions) never serves, so it has nothing to bind. Validate( new TelemetryOptions { Mode = TelemetryOptions.ModeGrpc, ApiKey = "k" }, "admin").Succeeded.ShouldBeTrue(); } [Fact] public void Grpc_with_no_roles_does_not_require_a_key() { // Fail-closed only applies to a node that actually carries a role — a roleless node hosts // nothing to protect. Validate(new TelemetryOptions { Mode = TelemetryOptions.ModeGrpc, ApiKey = "" }) .Succeeded.ShouldBeTrue(); } [Fact] public void Dps_ignores_the_empty_grpc_surface() { Validate( new TelemetryOptions { Mode = TelemetryOptions.ModeDps, GrpcListenPort = 0, ApiKey = "" }, "driver").Succeeded.ShouldBeTrue(); } } /// /// Mirrors for the central dial-side options. /// public class TelemetryDialOptionsValidatorTests { private static ValidateOptionsResult Validate(TelemetryDialOptions o) => new TelemetryDialOptionsValidator().Validate(TelemetryDialOptions.SectionName, o); [Fact] public void Default_options_are_valid() { Validate(new TelemetryDialOptions()).Succeeded.ShouldBeTrue(); } [Fact] public void Unknown_mode_fails() { var result = Validate(new TelemetryDialOptions { Mode = "poll" }); result.Failed.ShouldBeTrue(); result.FailureMessage.ShouldContain("poll"); } [Fact] public void Grpc_with_empty_key_fails() { var result = Validate(new TelemetryDialOptions { Mode = TelemetryDialOptions.ModeGrpc, ApiKey = "" }); result.Failed.ShouldBeTrue(); result.FailureMessage.ShouldContain(nameof(TelemetryDialOptions.ApiKey)); } [Fact] public void Grpc_with_a_key_is_valid() { Validate(new TelemetryDialOptions { Mode = TelemetryDialOptions.ModeGrpc, ApiKey = "k" }) .Succeeded.ShouldBeTrue(); } [Fact] public void Dps_ignores_the_empty_key() { Validate(new TelemetryDialOptions { Mode = TelemetryDialOptions.ModeDps, ApiKey = "" }) .Succeeded.ShouldBeTrue(); } }