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:
@@ -82,9 +82,6 @@ public partial class KpiTrendChart
|
||||
/// <summary>Exactly one sample — show the single-sample note, not a polyline.</summary>
|
||||
private bool HasSingleSample => IsAvailable && Series.Count == 1;
|
||||
|
||||
/// <summary>Available + ≥1 point but not a full chart, or unavailable / empty.</summary>
|
||||
private bool ShowPlaceholder => !IsAvailable || Series.Count == 0;
|
||||
|
||||
/// <summary>
|
||||
/// Stable Playwright hook: <c>kpi-trend-<slug></c> where the slug is the
|
||||
/// title lowercased with each run of non-alphanumerics collapsed to a single
|
||||
@@ -175,13 +172,17 @@ public partial class KpiTrendChart
|
||||
var p = Series[i];
|
||||
|
||||
// X — by time fraction, or even index spacing when all timestamps equal.
|
||||
// n >= 2 is guaranteed by the HasChart precondition (Series.Count >= 2),
|
||||
// so the (n - 1) divisor is always ≥ 1 and the n == 1 arm is unreachable.
|
||||
double xFrac = timeSpanTicks > 0
|
||||
? (p.BucketStartUtc.Ticks - firstTicks) / timeSpanTicks
|
||||
: (n == 1 ? 0.0 : (double)i / (n - 1));
|
||||
: (double)i / (n - 1);
|
||||
var x = PadX + (xFrac * plotW);
|
||||
|
||||
// Y — baseline at 0, top at max. Flat at baseline when max == 0.
|
||||
double yFrac = max > 0 ? p.Value / max : 0.0;
|
||||
// Clamp to non-negative so a stale negative Value cannot push a point
|
||||
// above the baseline or outside the viewBox.
|
||||
double yFrac = max > 0 ? Math.Max(0, p.Value) / max : 0.0;
|
||||
var y = baselineY - (yFrac * plotH);
|
||||
|
||||
if (i > 0)
|
||||
|
||||
@@ -27,7 +27,8 @@ public static class KpiSeriesBucketer
|
||||
/// An <see cref="IReadOnlyList{T}"/> of at most <paramref name="maxPoints"/> bucketed points,
|
||||
/// ordered by <see cref="KpiSeriesPoint.BucketStartUtc"/> ascending.
|
||||
/// Returns <paramref name="raw"/> unchanged (same reference) when
|
||||
/// <c>raw.Count <= maxPoints</c>.
|
||||
/// <c>raw.Count <= maxPoints</c>; callers must not mutate the underlying
|
||||
/// collection in that case, as it is the same object passed in.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException">
|
||||
/// Thrown when <paramref name="maxPoints"/> < 2 or
|
||||
|
||||
@@ -87,9 +87,9 @@ public class KpiHistoryRecorderActor : ReceiveActor, IWithTimers
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
|
||||
Receive<SampleTick>(_ => HandleSampleTick());
|
||||
Receive<SampleComplete>(_ => { });
|
||||
Receive<SampleComplete>(_ => { }); // best-effort: no actor state to reset on completion
|
||||
Receive<PurgeTick>(_ => HandlePurgeTick());
|
||||
Receive<PurgeComplete>(_ => { });
|
||||
Receive<PurgeComplete>(_ => { }); // best-effort: no actor state to reset on completion
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
Reference in New Issue
Block a user