using System; using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Stability; namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Host.Tests; [Trait("Category", "Unit")] public sealed class RecyclePolicyTests { [Fact] public void First_soft_recycle_is_allowed() { var p = new RecyclePolicy(); p.TryRequestSoftRecycle(DateTime.UtcNow, out var reason).ShouldBeTrue(); reason.ShouldBeNull(); } [Fact] public void Second_soft_recycle_within_cap_is_blocked() { var p = new RecyclePolicy(); var t0 = DateTime.UtcNow; p.TryRequestSoftRecycle(t0, out _).ShouldBeTrue(); p.TryRequestSoftRecycle(t0.AddMinutes(30), out var reason).ShouldBeFalse(); reason.ShouldContain("frequency cap"); } [Fact] public void Recycle_after_cap_elapses_is_allowed_again() { var p = new RecyclePolicy(); var t0 = DateTime.UtcNow; p.TryRequestSoftRecycle(t0, out _).ShouldBeTrue(); p.TryRequestSoftRecycle(t0.AddHours(1).AddMinutes(1), out _).ShouldBeTrue(); } [Fact] public void Scheduled_recycle_fires_once_per_day_at_local_3am() { var p = new RecyclePolicy(); var last = DateTime.MinValue; p.ShouldSoftRecycleScheduled(new DateTime(2026, 4, 17, 2, 59, 0), ref last).ShouldBeFalse(); p.ShouldSoftRecycleScheduled(new DateTime(2026, 4, 17, 3, 0, 0), ref last).ShouldBeTrue(); p.ShouldSoftRecycleScheduled(new DateTime(2026, 4, 17, 3, 30, 0), ref last).ShouldBeFalse( "already fired today"); p.ShouldSoftRecycleScheduled(new DateTime(2026, 4, 18, 3, 0, 0), ref last).ShouldBeTrue( "next day fires again"); } }