using Microsoft.Extensions.Options; namespace ZB.MOM.WW.ScadaBridge.SiteRuntime.Tests; /// /// values size the staggered Instance-Actor /// startup batches, the dedicated script-execution thread scheduler, and the /// site-wide stream buffer. A zero/negative count crashes actor construction with /// an opaque ; these tests assert the /// rejects a bad /// ScadaBridge:SiteRuntime section with a clear, key-naming message. /// public class SiteRuntimeOptionsValidatorTests { private static ValidateOptionsResult Validate(SiteRuntimeOptions options) => new SiteRuntimeOptionsValidator().Validate(Options.DefaultName, options); [Fact] public void DefaultOptions_AreValid() { var result = Validate(new SiteRuntimeOptions()); Assert.True(result.Succeeded, result.FailureMessage); } [Fact] public void ZeroStartupBatchSize_IsRejected() { var result = Validate(new SiteRuntimeOptions { StartupBatchSize = 0 }); Assert.True(result.Failed); Assert.Contains("StartupBatchSize", result.FailureMessage); } [Fact] public void ZeroScriptExecutionThreadCount_IsRejected() { var result = Validate(new SiteRuntimeOptions { ScriptExecutionThreadCount = 0 }); Assert.True(result.Failed); Assert.Contains("ScriptExecutionThreadCount", result.FailureMessage); } [Fact] public void ZeroTagSubscribeRetryIntervalMs_IsRejected() { var result = Validate(new SiteRuntimeOptions { TagSubscribeRetryIntervalMs = 0 }); Assert.True(result.Failed); Assert.Contains("TagSubscribeRetryIntervalMs", result.FailureMessage); } [Fact] public void NegativeStuckScriptGraceMs_IsRejected() { var result = Validate(new SiteRuntimeOptions { StuckScriptGraceMs = -1 }); Assert.True(result.Failed); Assert.Contains("StuckScriptGraceMs", result.FailureMessage); } [Fact] public void ZeroStreamBufferSize_IsRejected() { var result = Validate(new SiteRuntimeOptions { StreamBufferSize = 0 }); Assert.True(result.Failed); Assert.Contains("StreamBufferSize", result.FailureMessage); } [Fact] public void NegativeStartupBatchDelayMs_IsRejected() { var result = Validate(new SiteRuntimeOptions { StartupBatchDelayMs = -1 }); Assert.True(result.Failed); Assert.Contains("StartupBatchDelayMs", result.FailureMessage); } [Fact] public void ZeroAlarmPublishQueueCapacity_IsRejected() { var result = Validate(new SiteRuntimeOptions { AlarmPublishQueueCapacity = 0 }); Assert.True(result.Failed); Assert.Contains("AlarmPublishQueueCapacity", result.FailureMessage); } // ── WP3.1 options ────────────────────────────────────────────────────────────── /// /// The ceiling is the upper bound of the instance-scaled pool; below the floor it would /// silently shrink the configured pool instead of growing it. /// [Fact] public void ScriptExecutionMaxThreadCountBelowThreadCount_IsRejected() { var result = Validate(new SiteRuntimeOptions { ScriptExecutionThreadCount = 8, ScriptExecutionMaxThreadCount = 4 }); Assert.True(result.Failed); Assert.Contains("ScriptExecutionMaxThreadCount", result.FailureMessage); } [Fact] public void EqualScriptExecutionThreadCountAndMax_IsAccepted() { var result = Validate(new SiteRuntimeOptions { ScriptExecutionThreadCount = 8, ScriptExecutionMaxThreadCount = 8 }); Assert.True(result.Succeeded, result.FailureMessage); } /// A zero gate would park every Expression trigger on the node forever. [Fact] public void ZeroTriggerEvalMaxConcurrency_IsRejected() { var result = Validate(new SiteRuntimeOptions { TriggerEvalMaxConcurrency = 0 }); Assert.True(result.Failed); Assert.Contains("TriggerEvalMaxConcurrency", result.FailureMessage); } [Fact] public void ZeroTriggerEvalTimeoutSeconds_IsRejected() { var result = Validate(new SiteRuntimeOptions { TriggerEvalTimeoutSeconds = 0 }); Assert.True(result.Failed); Assert.Contains("TriggerEvalTimeoutSeconds", result.FailureMessage); } /// A zero cap would shed every trigger, silently disabling all scripts. [Fact] public void ZeroMaxConcurrentRunsPerScript_IsRejected() { var result = Validate(new SiteRuntimeOptions { MaxConcurrentRunsPerScript = 0 }); Assert.True(result.Failed); Assert.Contains("MaxConcurrentRunsPerScript", result.FailureMessage); } }