using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Core.Resilience; namespace ZB.MOM.WW.OtOpcUa.Core.Tests.Resilience; [Trait("Category", "Unit")] public sealed class DriverResilienceStatusTrackerTests { private static readonly DateTime Now = new(2026, 4, 19, 12, 0, 0, DateTimeKind.Utc); [Fact] public void TryGet_Returns_Null_Before_AnyWrite() { var tracker = new DriverResilienceStatusTracker(); tracker.TryGet("drv", "host").ShouldBeNull(); } [Fact] public void RecordFailure_Accumulates_ConsecutiveFailures() { var tracker = new DriverResilienceStatusTracker(); tracker.RecordFailure("drv", "host", Now); tracker.RecordFailure("drv", "host", Now.AddSeconds(1)); tracker.RecordFailure("drv", "host", Now.AddSeconds(2)); tracker.TryGet("drv", "host")!.ConsecutiveFailures.ShouldBe(3); } [Fact] public void RecordSuccess_Resets_ConsecutiveFailures() { var tracker = new DriverResilienceStatusTracker(); tracker.RecordFailure("drv", "host", Now); tracker.RecordFailure("drv", "host", Now.AddSeconds(1)); tracker.RecordSuccess("drv", "host", Now.AddSeconds(2)); tracker.TryGet("drv", "host")!.ConsecutiveFailures.ShouldBe(0); } [Fact] public void RecordBreakerOpen_Populates_LastBreakerOpenUtc() { var tracker = new DriverResilienceStatusTracker(); tracker.RecordBreakerOpen("drv", "host", Now); tracker.TryGet("drv", "host")!.LastBreakerOpenUtc.ShouldBe(Now); } [Fact] public void RecordRecycle_Populates_LastRecycleUtc() { var tracker = new DriverResilienceStatusTracker(); tracker.RecordRecycle("drv", "host", Now); tracker.TryGet("drv", "host")!.LastRecycleUtc.ShouldBe(Now); } [Fact] public void RecordFootprint_CapturesBaselineAndCurrent() { var tracker = new DriverResilienceStatusTracker(); tracker.RecordFootprint("drv", "host", baselineBytes: 100_000_000, currentBytes: 150_000_000, Now); var snap = tracker.TryGet("drv", "host")!; snap.BaselineFootprintBytes.ShouldBe(100_000_000); snap.CurrentFootprintBytes.ShouldBe(150_000_000); } [Fact] public void DifferentHosts_AreIndependent() { var tracker = new DriverResilienceStatusTracker(); tracker.RecordFailure("drv", "host-a", Now); tracker.RecordFailure("drv", "host-b", Now); tracker.RecordSuccess("drv", "host-a", Now.AddSeconds(1)); tracker.TryGet("drv", "host-a")!.ConsecutiveFailures.ShouldBe(0); tracker.TryGet("drv", "host-b")!.ConsecutiveFailures.ShouldBe(1); } [Fact] public void Snapshot_ReturnsAll_TrackedPairs() { var tracker = new DriverResilienceStatusTracker(); tracker.RecordFailure("drv-1", "host-a", Now); tracker.RecordFailure("drv-1", "host-b", Now); tracker.RecordFailure("drv-2", "host-a", Now); var snapshot = tracker.Snapshot(); snapshot.Count.ShouldBe(3); } [Fact] public void ConcurrentWrites_DoNotLose_Failures() { var tracker = new DriverResilienceStatusTracker(); Parallel.For(0, 500, _ => tracker.RecordFailure("drv", "host", Now)); tracker.TryGet("drv", "host")!.ConsecutiveFailures.ShouldBe(500); } }