refactor(kpi): K4/K10/K12 review fixups — test data-race + faulted-tick liveness, dead-branch/unused removal, NaN-guard assertions, value clamp + doc

This commit is contained in:
Joseph Doherty
2026-06-17 20:15:47 -04:00
parent 5613a5efb7
commit cb2a516187
6 changed files with 105 additions and 39 deletions
@@ -190,33 +190,47 @@ public class KpiSeriesBucketerTests
[Fact]
public void Bucket_PointAtToUtc_LandsInLastBucket()
{
// 60-minute window / 3 buckets → 20 min each.
// A point exactly at T(60) = toUtc must go to bucket 2, not overflow.
// 60-minute window / 2 buckets → 30 min each.
// A point exactly at T(60) = toUtc must go to bucket 1 (the last bucket),
// not overflow to an out-of-range index.
// T(10) → bucket 0: [0, 30); T(35) → bucket 1: [30, 60]; T(60) → bucket 1.
var raw = new[]
{
new KpiSeriesPoint(T(10), 1.0),
new KpiSeriesPoint(T(30), 2.0),
new KpiSeriesPoint(T(60), 99.0), // right edge — must land in last bucket
};
// raw.Count (3) == maxPoints (3) so it normally returns as-is;
// use maxPoints=2 to force downsampling and expose the edge behaviour.
// Window: [T(0), T(60)], 2 buckets → 30 min each.
// T(10) → bucket 0, T(30) → bucket 1, T(60) → bucket 1 (last).
var raw2 = new[]
{
new KpiSeriesPoint(T(10), 5.0),
new KpiSeriesPoint(T(35), 6.0),
new KpiSeriesPoint(T(60), 7.0), // exactly toUtc → bucket 1
new KpiSeriesPoint(T(60), 7.0), // exactly toUtc → must land in last bucket
};
var result = KpiSeriesBucketer.Bucket(raw2, T(0), T(60), maxPoints: 2);
var result = KpiSeriesBucketer.Bucket(raw, T(0), T(60), maxPoints: 2);
Assert.Equal(2, result.Count);
// Bucket 1 holds both T(35) and T(60); T(60) is later → wins.
Assert.Equal(7.0, result[1].Value);
}
[Fact]
public void Bucket_ThreeBucketWindow_PointAtToUtc_LandsInLastBucket()
{
// 60-minute window / 3 buckets → 20 min each.
// A point exactly at T(60) = toUtc must land in bucket 2 (the last),
// not overflow. Use 4 raw points so downsampling is forced (4 > 3).
// T(5) → bucket 0: [0,20); T(25) → bucket 1: [20,40);
// T(45) → bucket 2: [40,60]; T(60) → bucket 2 (right edge, later → wins).
var raw = new[]
{
new KpiSeriesPoint(T(5), 1.0),
new KpiSeriesPoint(T(25), 2.0),
new KpiSeriesPoint(T(45), 3.0),
new KpiSeriesPoint(T(60), 99.0), // right edge — must land in last bucket
};
var result = KpiSeriesBucketer.Bucket(raw, T(0), T(60), maxPoints: 3);
Assert.Equal(3, result.Count);
// Bucket 2 holds T(45)=3.0 and T(60)=99.0; T(60) is later → wins.
Assert.Equal(99.0, result[2].Value);
}
// -----------------------------------------------------------------------
// Empty buckets omitted — no gap-filling
// -----------------------------------------------------------------------