using Shouldly; using Xunit; namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests; /// Gitea #694: — the pure window arithmetic of ServerHistorian:EventReadHorizon. public sealed class EventHorizonTests { private static readonly DateTime Now = new(2026, 10, 13, 12, 0, 0, DateTimeKind.Utc); private static readonly TimeSpan H = TimeSpan.FromDays(30); private static readonly DateTime Floor = Now - H; [Fact] public void No_horizon_changes_nothing() { var (s, e) = (DateTime.MinValue, Now); EventHorizon.TryClamp(ref s, ref e, reverse: false, TimeSpan.Zero, Now).ShouldBeTrue(); (s, e).ShouldBe((DateTime.MinValue, Now)); EventHorizon.TryClamp(ref s, ref e, reverse: false, TimeSpan.FromDays(-1), Now).ShouldBeTrue(); (s, e).ShouldBe((DateTime.MinValue, Now)); } [Fact] public void A_forward_window_starts_at_the_floor() { var (s, e) = (DateTime.UnixEpoch, Now); EventHorizon.TryClamp(ref s, ref e, reverse: false, H, Now).ShouldBeTrue(); (s, e).ShouldBe((Floor, Now)); var (s2, e2) = (Floor.AddDays(1), Now); EventHorizon.TryClamp(ref s2, ref e2, reverse: false, H, Now).ShouldBeTrue(); s2.ShouldBe(Floor.AddDays(1)); // already inside: unchanged } [Fact] public void A_reverse_window_keeps_the_floor_instant() { var (s, e) = (Now, new DateTime(0, DateTimeKind.Utc)); EventHorizon.TryClamp(ref s, ref e, reverse: true, H, Now).ShouldBeTrue(); (s, e).ShouldBe((Now, Floor.AddTicks(-1))); // (end, start] — the floor itself is read } [Fact] public void A_window_wholly_before_the_floor_is_empty() { var (s, e) = (Floor.AddDays(-2), Floor); EventHorizon.TryClamp(ref s, ref e, reverse: false, H, Now).ShouldBeFalse(); // [start, end) ends at the floor var (rs, re) = (Floor.AddTicks(-1), Floor.AddDays(-2)); EventHorizon.TryClamp(ref rs, ref re, reverse: true, H, Now).ShouldBeFalse(); var (ok, oe) = (Floor, Floor.AddDays(-2)); EventHorizon.TryClamp(ref ok, ref oe, reverse: true, H, Now).ShouldBeTrue(); // (…, floor] holds the floor } [Fact] public void A_horizon_longer_than_the_calendar_is_no_horizon() { var (s, e) = (DateTime.MinValue, Now); EventHorizon.TryClamp(ref s, ref e, reverse: false, TimeSpan.MaxValue, Now).ShouldBeTrue(); s.ShouldBe(DateTime.MinValue); } }