using Microsoft.Extensions.Options; namespace ZB.MOM.WW.ScadaBridge.SiteEventLogging.Tests; /// /// retention/purge values drive the daily purge /// PeriodicTimer (zero/negative trips its constructor) and the paging bounds gate /// query result sizes. These tests assert the /// rejects a bad /// ScadaBridge:SiteEventLog section with a clear, key-naming message. /// public class SiteEventLogOptionsValidatorTests { private static ValidateOptionsResult Validate(SiteEventLogOptions options) => new SiteEventLogOptionsValidator().Validate(Options.DefaultName, options); [Fact] public void DefaultOptions_AreValid() { var result = Validate(new SiteEventLogOptions()); Assert.True(result.Succeeded, result.FailureMessage); } [Fact] public void ZeroRetentionDays_IsRejected() { var result = Validate(new SiteEventLogOptions { RetentionDays = 0 }); Assert.True(result.Failed); Assert.Contains("RetentionDays", result.FailureMessage); } [Fact] public void ZeroPurgeInterval_IsRejected() { var result = Validate(new SiteEventLogOptions { PurgeInterval = TimeSpan.Zero }); Assert.True(result.Failed); Assert.Contains("PurgeInterval", result.FailureMessage); } [Fact] public void MaxQueryPageSize_BelowQueryPageSize_IsRejected() { var result = Validate(new SiteEventLogOptions { QueryPageSize = 500, MaxQueryPageSize = 100 }); Assert.True(result.Failed); Assert.Contains("MaxQueryPageSize", result.FailureMessage); } // ── Register row 34: the per-tick purge ceiling ── [Fact] public void NegativeMaxPurgedRowsPerTick_IsRejected() { // 0 already means "unlimited" explicitly, so a negative value is a typo, not a synonym. // Accepting it would silently remove the only bound on a purge tick's CDC oplog burst. var result = Validate(new SiteEventLogOptions { MaxPurgedRowsPerTick = -1 }); Assert.True(result.Failed); Assert.Contains("MaxPurgedRowsPerTick", result.FailureMessage); } [Fact] public void ZeroMaxPurgedRowsPerTick_IsAccepted() { // The documented escape hatch back to unbounded purges. var result = Validate(new SiteEventLogOptions { MaxPurgedRowsPerTick = 0 }); Assert.True(result.Succeeded, result.FailureMessage); } [Fact] public void ZeroPurgeCatchUpInterval_IsRejected() { // It is a Task.Delay period on the purge loop; zero would spin a ceiling-limited node. var result = Validate(new SiteEventLogOptions { PurgeCatchUpInterval = TimeSpan.Zero }); Assert.True(result.Failed); Assert.Contains("PurgeCatchUpInterval", result.FailureMessage); } [Fact] public void PurgeCatchUpInterval_LongerThanPurgeInterval_IsRejected() { // The catch-up is an ACCELERATED retry; a longer value would make hitting the ceiling // delay convergence instead of hastening it — the exact inverse of its purpose. var result = Validate(new SiteEventLogOptions { PurgeInterval = TimeSpan.FromMinutes(10), PurgeCatchUpInterval = TimeSpan.FromHours(1), }); Assert.True(result.Failed); Assert.Contains("PurgeCatchUpInterval", result.FailureMessage); } }