using Microsoft.Extensions.Options; namespace ZB.MOM.WW.ScadaBridge.Communication.Tests; /// /// Regression: timeouts feed per-pattern Ask /// deadlines and gRPC keepalive/stream-lifetime settings; a zero/negative value /// (or a non-positive GrpcMaxConcurrentStreams) must be rejected at startup /// by an with a clear, key-naming message /// rather than surfacing at first Ask/gRPC use. /// public class CommunicationOptionsValidatorTests { private static ValidateOptionsResult Validate(CommunicationOptions options) => new CommunicationOptionsValidator().Validate(Options.DefaultName, options); [Fact] public void DefaultOptions_AreValid() { var result = Validate(new CommunicationOptions()); Assert.True(result.Succeeded, result.FailureMessage); } [Fact] public void ZeroDeploymentTimeout_IsRejected() { var result = Validate(new CommunicationOptions { DeploymentTimeout = TimeSpan.Zero }); Assert.True(result.Failed); Assert.Contains("DeploymentTimeout", result.FailureMessage); } [Fact] public void NonPositiveGrpcMaxConcurrentStreams_IsRejected() { var result = Validate(new CommunicationOptions { GrpcMaxConcurrentStreams = 0 }); Assert.True(result.Failed); Assert.Contains("GrpcMaxConcurrentStreams", result.FailureMessage); } // ── Aggregated live alarm cache options (plan #10, Task 6) ─────────────────── [Fact] public void ZeroLiveAlarmCacheLinger_IsValid() { // Zero linger = stop the aggregator immediately when the last viewer leaves. var result = Validate(new CommunicationOptions { LiveAlarmCacheLinger = TimeSpan.Zero }); Assert.True(result.Succeeded, result.FailureMessage); } [Fact] public void NegativeLiveAlarmCacheLinger_IsRejected() { var result = Validate(new CommunicationOptions { LiveAlarmCacheLinger = TimeSpan.FromSeconds(-1) }); Assert.True(result.Failed); Assert.Contains("LiveAlarmCacheLinger", result.FailureMessage); } [Fact] public void ZeroLiveAlarmCacheReconcileInterval_IsRejected() { var result = Validate(new CommunicationOptions { LiveAlarmCacheReconcileInterval = TimeSpan.Zero }); Assert.True(result.Failed); Assert.Contains("LiveAlarmCacheReconcileInterval", result.FailureMessage); } [Fact] public void ZeroLiveAlarmCacheSeedConcurrency_IsRejected() { var result = Validate(new CommunicationOptions { LiveAlarmCacheSeedConcurrency = 0 }); Assert.True(result.Failed); Assert.Contains("LiveAlarmCacheSeedConcurrency", result.FailureMessage); } [Fact] public void ExcessiveLiveAlarmCacheSeedConcurrency_IsRejected() { var result = Validate(new CommunicationOptions { LiveAlarmCacheSeedConcurrency = 65 }); Assert.True(result.Failed); Assert.Contains("LiveAlarmCacheSeedConcurrency", result.FailureMessage); } [Fact] public void ZeroLiveAlarmCacheMaxSubscribersPerSite_IsRejected() { var result = Validate(new CommunicationOptions { LiveAlarmCacheMaxSubscribersPerSite = 0 }); Assert.True(result.Failed); Assert.Contains("LiveAlarmCacheMaxSubscribersPerSite", result.FailureMessage); } }