using Shouldly; using Xunit; using ZB.MOM.WW.OtOpcUa.Core.Abstractions; using static ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common.Tests.Fixture; namespace ZB.MOM.WW.OtOpcUa.Driver.Historian.Sql.Common.Tests; public sealed class BucketPlanTests { [Fact] public void BucketPlan_rejects_more_than_100k_buckets() { var ex = Should.Throw(() => BucketPlan.Create(DateTime.UnixEpoch, DateTime.UnixEpoch.AddSeconds(100_001), TimeSpan.FromSeconds(1))); ex.ParamName.ShouldBe("interval"); } [Fact] public void Exactly_100k_one_tick_buckets_are_allowed() { var p = BucketPlan.Create(At(0), At(0).AddTicks(100_000), TimeSpan.FromTicks(1)); p.BucketCount.ShouldBe(100_000); p.IntervalTicks.ShouldBe(1); } [Fact] public void A_partial_last_interval_is_its_own_bucket() { var p = BucketPlan.Create(At(0), At(50), TimeSpan.FromSeconds(20)); p.BucketCount.ShouldBe(3); p.BucketStart(2).ShouldBe(At(40)); p.BucketStart(2).Kind.ShouldBe(DateTimeKind.Utc); } [Fact] public void Zero_or_oversized_interval_is_one_bucket_and_an_empty_window_none() { BucketPlan.Create(At(0), At(80), TimeSpan.Zero).BucketCount.ShouldBe(1); var wide = BucketPlan.Create(At(0), At(80), TimeSpan.FromDays(1)); wide.BucketCount.ShouldBe(1); wide.IntervalTicks.ShouldBe(80 * TimeSpan.TicksPerSecond); BucketPlan.Create(At(0), At(0), TimeSpan.FromSeconds(1)).BucketCount.ShouldBe(0); } [Fact] public void A_reverse_window_is_planned_forward_and_assembled_descending() { var p = BucketPlan.Create(At(80), At(0), TimeSpan.FromSeconds(20)); p.Reversed.ShouldBeTrue(); p.StartUtc.ShouldBe(At(0)); p.EndUtc.ShouldBe(At(80)); var s = p.Assemble(new Dictionary(), t => new DataValueSnapshot(null, 0, t, t)); s.Select(x => x.SourceTimestampUtc).ShouldBe([At(60), At(40), At(20), At(0)]); } [Fact] public void Assemble_fills_missing_buckets_with_the_empty_factory() { var p = BucketPlan.Create(At(0), At(60), TimeSpan.FromSeconds(20)); var hit = new DataValueSnapshot(7d, 0, At(20), At(20)); var s = p.Assemble(new Dictionary { [1] = hit }, t => new DataValueSnapshot(null, 1u, t, t)); s.Select(x => x.Value).ShouldBe([null, (object?)7d, null]); s[0].SourceTimestampUtc.ShouldBe(At(0)); s[2].SourceTimestampUtc.ShouldBe(At(40)); } [Fact] public void StartUnixTicks_is_ticks_since_the_unix_epoch() => BucketPlan.Create(DateTime.UnixEpoch.AddTicks(1234567), DateTime.UnixEpoch.AddSeconds(10), TimeSpan.Zero) .StartUnixTicks.ShouldBe(1234567L); }