docs(tests): fix CommentChecker findings across tests/ — 13,392 → 0 (repo-wide scan clean)
Comment/XML-doc changes only: - add summaries to ~10k test methods, fixtures, fakes and helpers (+ <param> on theories, <returns> on async Task methods, <inheritdoc/> on interface implementations the checker can resolve) - strip ~2.6k project-tracking references (WP*, P*-*, row-NN, Task N, #NN, Component-0NN) from comments, keeping the explanatory text; reword false positives in comments only - real docs on fakes implementing BCL/library interfaces (IDisposable, ILogger, IOptionsMonitor, IAsyncLifetime) - reword the last 5 src/ false positives (CWE-1236, failures-1, year-0001, M365) Only non-comment edits: four one-line nested classes split across lines so members can carry docs. Build 0 errors / 0 warnings.
This commit is contained in:
@@ -3,7 +3,7 @@ using ZB.MOM.WW.ScadaBridge.KpiHistory;
|
||||
namespace ZB.MOM.WW.ScadaBridge.KpiHistory.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// K3 unit tests for <see cref="KpiHistoryOptionsValidator"/>: the default
|
||||
/// Unit tests for <see cref="KpiHistoryOptionsValidator"/>: the default
|
||||
/// options must validate, and each rule (SampleInterval / RetentionDays /
|
||||
/// PurgeInterval / DefaultMaxSeriesPoints) must reject its out-of-range values.
|
||||
/// </summary>
|
||||
@@ -11,6 +11,7 @@ public class KpiHistoryOptionsValidatorTests
|
||||
{
|
||||
private static KpiHistoryOptionsValidator NewValidator() => new();
|
||||
|
||||
/// <summary>Verifies that a zero source timeout fails validation.</summary>
|
||||
[Fact]
|
||||
public void Validate_SourceTimeout_Zero_Fails()
|
||||
{
|
||||
@@ -19,6 +20,7 @@ public class KpiHistoryOptionsValidatorTests
|
||||
Assert.Contains("SourceTimeout", result.FailureMessage);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the default options pass validation.</summary>
|
||||
[Fact]
|
||||
public void Validate_Defaults_Pass()
|
||||
{
|
||||
@@ -30,6 +32,7 @@ public class KpiHistoryOptionsValidatorTests
|
||||
// SampleInterval > TimeSpan.Zero
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/// <summary>Verifies that a zero sample interval fails validation.</summary>
|
||||
[Fact]
|
||||
public void Validate_SampleInterval_Zero_Fails()
|
||||
{
|
||||
@@ -42,6 +45,7 @@ public class KpiHistoryOptionsValidatorTests
|
||||
f => f.Contains(nameof(KpiHistoryOptions.SampleInterval), StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a negative sample interval fails validation.</summary>
|
||||
[Fact]
|
||||
public void Validate_SampleInterval_Negative_Fails()
|
||||
{
|
||||
@@ -58,6 +62,8 @@ public class KpiHistoryOptionsValidatorTests
|
||||
// RetentionDays in [1, 3650]
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/// <summary>Verifies that an in-range retention period passes validation.</summary>
|
||||
/// <param name="value">The option value under test.</param>
|
||||
[Theory]
|
||||
[InlineData(1)] // min
|
||||
[InlineData(90)] // default
|
||||
@@ -71,6 +77,8 @@ public class KpiHistoryOptionsValidatorTests
|
||||
Assert.True(NewValidator().Validate(null, opts).Succeeded);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an out-of-range retention period fails validation.</summary>
|
||||
/// <param name="value">The option value under test.</param>
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(3651)]
|
||||
@@ -90,6 +98,7 @@ public class KpiHistoryOptionsValidatorTests
|
||||
// PurgeInterval > TimeSpan.Zero
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/// <summary>Verifies that a zero purge interval fails validation.</summary>
|
||||
[Fact]
|
||||
public void Validate_PurgeInterval_Zero_Fails()
|
||||
{
|
||||
@@ -102,6 +111,7 @@ public class KpiHistoryOptionsValidatorTests
|
||||
f => f.Contains(nameof(KpiHistoryOptions.PurgeInterval), StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a negative purge interval fails validation.</summary>
|
||||
[Fact]
|
||||
public void Validate_PurgeInterval_Negative_Fails()
|
||||
{
|
||||
@@ -118,6 +128,8 @@ public class KpiHistoryOptionsValidatorTests
|
||||
// DefaultMaxSeriesPoints in [2, 5000]
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/// <summary>Verifies that an in-range default maximum series point count passes validation.</summary>
|
||||
/// <param name="value">The option value under test.</param>
|
||||
[Theory]
|
||||
[InlineData(2)] // min
|
||||
[InlineData(200)] // default
|
||||
@@ -128,6 +140,8 @@ public class KpiHistoryOptionsValidatorTests
|
||||
Assert.True(NewValidator().Validate(null, opts).Succeeded);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an out-of-range default maximum series point count fails validation.</summary>
|
||||
/// <param name="value">The option value under test.</param>
|
||||
[Theory]
|
||||
[InlineData(1)]
|
||||
[InlineData(5001)]
|
||||
@@ -147,6 +161,8 @@ public class KpiHistoryOptionsValidatorTests
|
||||
// RollupInterval > TimeSpan.Zero
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/// <summary>Verifies that a non-positive rollup interval fails validation.</summary>
|
||||
/// <param name="hours">The rollup interval in hours.</param>
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(-1)]
|
||||
@@ -165,6 +181,8 @@ public class KpiHistoryOptionsValidatorTests
|
||||
// RollupLookbackHours in [1, 168]
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/// <summary>Verifies that an in-range rollup lookback passes validation.</summary>
|
||||
/// <param name="value">The option value under test.</param>
|
||||
[Theory]
|
||||
[InlineData(1)] // min
|
||||
[InlineData(3)] // default
|
||||
@@ -175,6 +193,8 @@ public class KpiHistoryOptionsValidatorTests
|
||||
Assert.True(NewValidator().Validate(null, opts).Succeeded);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an out-of-range rollup lookback fails validation.</summary>
|
||||
/// <param name="value">The option value under test.</param>
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(169)]
|
||||
@@ -194,6 +214,8 @@ public class KpiHistoryOptionsValidatorTests
|
||||
// RollupRetentionDays in [1, 3650] AND >= RetentionDays
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/// <summary>Verifies that an in-range rollup retention period passes validation.</summary>
|
||||
/// <param name="value">The option value under test.</param>
|
||||
[Theory]
|
||||
[InlineData(90)] // == RetentionDays default
|
||||
[InlineData(365)] // default
|
||||
@@ -204,6 +226,8 @@ public class KpiHistoryOptionsValidatorTests
|
||||
Assert.True(NewValidator().Validate(null, opts).Succeeded);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an out-of-range rollup retention period fails validation.</summary>
|
||||
/// <param name="value">The option value under test.</param>
|
||||
[Theory]
|
||||
[InlineData(0)]
|
||||
[InlineData(3651)]
|
||||
@@ -219,6 +243,7 @@ public class KpiHistoryOptionsValidatorTests
|
||||
f => f.Contains(nameof(KpiHistoryOptions.RollupRetentionDays), StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a rollup retention shorter than the raw retention fails validation.</summary>
|
||||
[Fact]
|
||||
public void Validate_RollupRetentionDays_LessThanRetentionDays_Fails()
|
||||
{
|
||||
@@ -237,6 +262,7 @@ public class KpiHistoryOptionsValidatorTests
|
||||
&& f.Contains(nameof(KpiHistoryOptions.RetentionDays), StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a rollup retention equal to the raw retention passes validation.</summary>
|
||||
[Fact]
|
||||
public void Validate_RollupRetentionDays_EqualToRetentionDays_Passes()
|
||||
{
|
||||
@@ -249,6 +275,8 @@ public class KpiHistoryOptionsValidatorTests
|
||||
// RollupThresholdHours >= 24
|
||||
// ---------------------------------------------------------------------
|
||||
|
||||
/// <summary>Verifies that a rollup threshold at or above the minimum passes validation.</summary>
|
||||
/// <param name="value">The option value under test.</param>
|
||||
[Theory]
|
||||
[InlineData(24)] // min
|
||||
[InlineData(168)] // default
|
||||
@@ -259,6 +287,8 @@ public class KpiHistoryOptionsValidatorTests
|
||||
Assert.True(NewValidator().Validate(null, opts).Succeeded);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a rollup threshold below the minimum fails validation.</summary>
|
||||
/// <param name="value">The option value under test.</param>
|
||||
[Theory]
|
||||
[InlineData(23)]
|
||||
[InlineData(0)]
|
||||
@@ -274,6 +304,7 @@ public class KpiHistoryOptionsValidatorTests
|
||||
f => f.Contains(nameof(KpiHistoryOptions.RollupThresholdHours), StringComparison.Ordinal));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that violating every rule accumulates every failure message.</summary>
|
||||
[Fact]
|
||||
public void Validate_AllRulesViolated_AccumulatesEveryFailure()
|
||||
{
|
||||
|
||||
@@ -25,8 +25,10 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
/// </summary>
|
||||
private sealed class HealthySource : IKpiSampleSource
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Source => KpiSources.NotificationOutbox;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<KpiSample>> CollectAsync(
|
||||
DateTime capturedAtUtc, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -58,8 +60,10 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
/// <summary>A source whose <see cref="CollectAsync"/> always throws — must not abort the pass.</summary>
|
||||
private sealed class ThrowingSource : IKpiSampleSource
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public string Source => KpiSources.SiteCallAudit;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<KpiSample>> CollectAsync(
|
||||
DateTime capturedAtUtc, CancellationToken cancellationToken = default) =>
|
||||
throw new InvalidOperationException("simulated source failure");
|
||||
@@ -74,8 +78,10 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
{
|
||||
private int _callCount;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Source => KpiSources.SiteCallAudit;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<KpiSample>> CollectAsync(
|
||||
DateTime capturedAtUtc, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -116,6 +122,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
private DateTime? _latestRollupHour;
|
||||
private int _latestRollupHourQueryCount;
|
||||
|
||||
/// <summary>The samples recorded so far, as a snapshot.</summary>
|
||||
public IReadOnlyList<KpiSample> Recorded
|
||||
{
|
||||
get { lock (_gate) { return _recorded.ToArray(); } }
|
||||
@@ -123,6 +130,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
|
||||
// PurgeOlderThanAsync runs on a threadpool thread; guard the field with
|
||||
// the same _gate lock used by _recorded so test-thread reads are race-free.
|
||||
/// <summary>The cutoff passed to the most recent purge, or <see langword="null"/> if none.</summary>
|
||||
public DateTime? PurgeCutoff
|
||||
{
|
||||
get { lock (_gate) { return _purgeCutoff; } }
|
||||
@@ -158,6 +166,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
get { lock (_gate) { return _foldWindows.ToArray(); } }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RecordSamplesAsync(
|
||||
IReadOnlyCollection<KpiSample> samples, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -168,17 +177,20 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<KpiSeriesPoint>> GetRawSeriesAsync(
|
||||
string source, string metric, string scope, string? scopeKey,
|
||||
DateTime fromUtc, DateTime toUtc, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<IReadOnlyList<KpiSeriesPoint>>(Array.Empty<KpiSeriesPoint>());
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<int> PurgeOlderThanAsync(DateTime before, CancellationToken cancellationToken = default)
|
||||
{
|
||||
lock (_gate) { _purgeCutoff = before; }
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task FoldHourlyRollupsAsync(
|
||||
DateTime fromHourUtc, DateTime toHourUtc, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -192,11 +204,13 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<KpiSeriesPoint>> GetHourlySeriesAsync(
|
||||
string source, string metric, string scope, string? scopeKey,
|
||||
DateTime fromUtc, DateTime toUtc, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<IReadOnlyList<KpiSeriesPoint>>(Array.Empty<KpiSeriesPoint>());
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task PurgeRollupsOlderThanAsync(DateTime before, CancellationToken cancellationToken = default)
|
||||
{
|
||||
lock (_gate) { _rollupPurgeCutoff = before; }
|
||||
@@ -216,6 +230,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
get { lock (_gate) { return _latestRollupHourQueryCount; } }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<DateTime?> GetLatestRollupHourAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
lock (_gate)
|
||||
@@ -242,6 +257,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
/// <summary>Releases all blocked writes so the gated passes can complete.</summary>
|
||||
public void Release() => _release.Set();
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RecordSamplesAsync(
|
||||
IReadOnlyCollection<KpiSample> samples, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -251,26 +267,32 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
return Task.Run(() => _release.Wait(cancellationToken), cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<KpiSeriesPoint>> GetRawSeriesAsync(
|
||||
string source, string metric, string scope, string? scopeKey,
|
||||
DateTime fromUtc, DateTime toUtc, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<IReadOnlyList<KpiSeriesPoint>>(Array.Empty<KpiSeriesPoint>());
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<int> PurgeOlderThanAsync(DateTime before, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult(0);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task FoldHourlyRollupsAsync(
|
||||
DateTime fromHourUtc, DateTime toHourUtc, CancellationToken cancellationToken = default) =>
|
||||
Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<KpiSeriesPoint>> GetHourlySeriesAsync(
|
||||
string source, string metric, string scope, string? scopeKey,
|
||||
DateTime fromUtc, DateTime toUtc, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<IReadOnlyList<KpiSeriesPoint>>(Array.Empty<KpiSeriesPoint>());
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task PurgeRollupsOlderThanAsync(DateTime before, CancellationToken cancellationToken = default) =>
|
||||
Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<DateTime?> GetLatestRollupHourAsync(CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<DateTime?>(null);
|
||||
}
|
||||
@@ -291,18 +313,22 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
/// <summary>Releases all blocked folds so the gated passes can complete.</summary>
|
||||
public void Release() => _release.Set();
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RecordSamplesAsync(
|
||||
IReadOnlyCollection<KpiSample> samples, CancellationToken cancellationToken = default) =>
|
||||
Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<KpiSeriesPoint>> GetRawSeriesAsync(
|
||||
string source, string metric, string scope, string? scopeKey,
|
||||
DateTime fromUtc, DateTime toUtc, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<IReadOnlyList<KpiSeriesPoint>>(Array.Empty<KpiSeriesPoint>());
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<int> PurgeOlderThanAsync(DateTime before, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult(0);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task FoldHourlyRollupsAsync(
|
||||
DateTime fromHourUtc, DateTime toHourUtc, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -312,14 +338,17 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
return Task.Run(() => _release.Wait(cancellationToken), cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<KpiSeriesPoint>> GetHourlySeriesAsync(
|
||||
string source, string metric, string scope, string? scopeKey,
|
||||
DateTime fromUtc, DateTime toUtc, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<IReadOnlyList<KpiSeriesPoint>>(Array.Empty<KpiSeriesPoint>());
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task PurgeRollupsOlderThanAsync(DateTime before, CancellationToken cancellationToken = default) =>
|
||||
Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<DateTime?> GetLatestRollupHourAsync(CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<DateTime?>(null);
|
||||
}
|
||||
@@ -347,18 +376,22 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
get { lock (_gate) { return _lastFoldToHourUtc; } }
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RecordSamplesAsync(
|
||||
IReadOnlyCollection<KpiSample> samples, CancellationToken cancellationToken = default) =>
|
||||
Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<KpiSeriesPoint>> GetRawSeriesAsync(
|
||||
string source, string metric, string scope, string? scopeKey,
|
||||
DateTime fromUtc, DateTime toUtc, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<IReadOnlyList<KpiSeriesPoint>>(Array.Empty<KpiSeriesPoint>());
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<int> PurgeOlderThanAsync(DateTime before, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult(0);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task FoldHourlyRollupsAsync(
|
||||
DateTime fromHourUtc, DateTime toHourUtc, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -372,14 +405,17 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<KpiSeriesPoint>> GetHourlySeriesAsync(
|
||||
string source, string metric, string scope, string? scopeKey,
|
||||
DateTime fromUtc, DateTime toUtc, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<IReadOnlyList<KpiSeriesPoint>>(Array.Empty<KpiSeriesPoint>());
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task PurgeRollupsOlderThanAsync(DateTime before, CancellationToken cancellationToken = default) =>
|
||||
Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<DateTime?> GetLatestRollupHourAsync(CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<DateTime?>(null);
|
||||
}
|
||||
@@ -400,6 +436,8 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
private int _periodicFoldCount;
|
||||
private int _backfillCountAtFirstPeriodic = -1;
|
||||
|
||||
/// <summary>Initializes the repository with the width that distinguishes a periodic fold window from a backfill slice.</summary>
|
||||
/// <param name="periodicWindowWidth">The width of a periodic fold window.</param>
|
||||
public InterleaveFoldRepository(TimeSpan periodicWindowWidth) =>
|
||||
_periodicWindowWidth = periodicWindowWidth;
|
||||
|
||||
@@ -415,18 +453,22 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
/// <summary>Releases the first (held-in-flight) periodic fold so the deferred backfill resumes.</summary>
|
||||
public void ReleaseFirstPeriodic() => _firstPeriodicRelease.Release();
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RecordSamplesAsync(
|
||||
IReadOnlyCollection<KpiSample> samples, CancellationToken cancellationToken = default) =>
|
||||
Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<KpiSeriesPoint>> GetRawSeriesAsync(
|
||||
string source, string metric, string scope, string? scopeKey,
|
||||
DateTime fromUtc, DateTime toUtc, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<IReadOnlyList<KpiSeriesPoint>>(Array.Empty<KpiSeriesPoint>());
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<int> PurgeOlderThanAsync(DateTime before, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult(0);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task FoldHourlyRollupsAsync(
|
||||
DateTime fromHourUtc, DateTime toHourUtc, CancellationToken cancellationToken = default)
|
||||
{
|
||||
@@ -455,14 +497,17 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<KpiSeriesPoint>> GetHourlySeriesAsync(
|
||||
string source, string metric, string scope, string? scopeKey,
|
||||
DateTime fromUtc, DateTime toUtc, CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<IReadOnlyList<KpiSeriesPoint>>(Array.Empty<KpiSeriesPoint>());
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task PurgeRollupsOlderThanAsync(DateTime before, CancellationToken cancellationToken = default) =>
|
||||
Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<DateTime?> GetLatestRollupHourAsync(CancellationToken cancellationToken = default) =>
|
||||
Task.FromResult<DateTime?>(null);
|
||||
}
|
||||
@@ -500,7 +545,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Row-40 house pattern for an ABSENCE assertion (mirrors the Medium-wave
|
||||
/// House pattern for an ABSENCE assertion (mirrors the Medium-wave
|
||||
/// <c>SiteAlarmAggregatorActorTests.AssertHoldsFor</c> treatment): proves
|
||||
/// <paramref name="holds"/> keeps being true for the whole <paramref name="window"/> by
|
||||
/// polling, rather than a single blind <c>Thread.Sleep(window)</c> followed by one
|
||||
@@ -531,19 +576,24 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
private sealed class HangingSource(HangingState state) : IKpiSampleSource, IDisposable
|
||||
{
|
||||
private bool _called;
|
||||
/// <inheritdoc />
|
||||
public string Source => "hung";
|
||||
/// <inheritdoc />
|
||||
public Task<IReadOnlyList<KpiSample>> CollectAsync(DateTime capturedAtUtc, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_called = true;
|
||||
Interlocked.Increment(ref state.Calls);
|
||||
return state.Completion.Task; // deliberately ignores cancellation
|
||||
}
|
||||
/// <summary>Releases the resources held by this test class.</summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (_called) Interlocked.Increment(ref state.ActiveScopesDisposed);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a hung source neither blocks healthy sources nor loses scope ownership.</summary>
|
||||
/// <param name="replaceActor">Whether the recorder actor is replaced while the source is hung.</param>
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
@@ -595,6 +645,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
AwaitAssert(() => Assert.Equal(1, Volatile.Read(ref state.ActiveScopesDisposed)), TimeSpan.FromSeconds(2));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a sample tick writes the healthy source's samples and a throwing source does not abort the tick.</summary>
|
||||
[Fact]
|
||||
public void SampleTick_WritesHealthySourceSamples_AndThrowingSourceDoesNotAbortTick()
|
||||
{
|
||||
@@ -618,6 +669,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
interval: TimeSpan.FromMilliseconds(50));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a purge tick calls purge with a cutoff of UTC now minus the retention days.</summary>
|
||||
[Fact]
|
||||
public void PurgeTick_CallsPurgeWithCutoff_AtUtcNowMinusRetentionDays()
|
||||
{
|
||||
@@ -646,6 +698,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
interval: TimeSpan.FromMilliseconds(50));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a faulted tick does not crash the actor and a subsequent tick still runs.</summary>
|
||||
[Fact]
|
||||
public void FaultedTick_DoesNotCrashActor_AndSubsequentTickStillRuns()
|
||||
{
|
||||
@@ -671,8 +724,8 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
// in-flight guard after the faulted first pass — a tick that lands before the guard
|
||||
// clears is harmlessly skipped, and the next poll's tick runs. The recovered sample
|
||||
// being recorded proves the actor's message loop is still alive after a faulted pass.
|
||||
// (>= 1 rather than exactly-1: a later re-sent tick could run an extra recovering pass
|
||||
// once the guard clears, which is not what this test pins — KH-001's one-pass-per-tick
|
||||
// (>= 1 rather than exactly one: a later re-sent tick could run an extra recovering pass
|
||||
// once the guard clears, which is not what this test pins — the one-pass-per-tick
|
||||
// guard is pinned by OverlappingTick_WhileFirstPassInFlight_DoesNotStartSecondPass.)
|
||||
AwaitAssert(
|
||||
() =>
|
||||
@@ -684,10 +737,11 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
interval: TimeSpan.FromMilliseconds(50));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an overlapping tick while the first pass is in flight does not start a second pass.</summary>
|
||||
[Fact]
|
||||
public void OverlappingTick_WhileFirstPassInFlight_DoesNotStartSecondPass()
|
||||
{
|
||||
// KpiHistory-001 regression: the in-flight guard must coalesce a tick that arrives
|
||||
// Regression: the in-flight guard must coalesce a tick that arrives
|
||||
// while a prior sample pass is still awaiting its DB write. With a gated repository
|
||||
// holding the first write open, a second SampleTick must NOT spawn a second pass —
|
||||
// so RecordSamplesAsync is entered exactly once until the gate is released.
|
||||
@@ -727,9 +781,10 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// Hourly rollup fold (Task 4)
|
||||
// Hourly rollup fold
|
||||
// =====================================================================
|
||||
|
||||
/// <summary>Verifies that a rollup tick folds the trailing lookback window with the in-progress hour excluded.</summary>
|
||||
[Fact]
|
||||
public void RollupTick_FoldsTrailingLookback_WithInProgressHourExcluded()
|
||||
{
|
||||
@@ -779,6 +834,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
interval: TimeSpan.FromMilliseconds(50));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a purge tick purges both raw samples and rollups with their own cutoffs.</summary>
|
||||
[Fact]
|
||||
public void PurgeTick_PurgesBothRawSamplesAndRollups_WithTheirOwnCutoffs()
|
||||
{
|
||||
@@ -818,6 +874,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
interval: TimeSpan.FromMilliseconds(50));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that an overlapping rollup tick while the first fold is in flight does not start a second fold.</summary>
|
||||
[Fact]
|
||||
public void OverlappingRollupTick_WhileFirstFoldInFlight_DoesNotStartSecondFold()
|
||||
{
|
||||
@@ -856,6 +913,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
interval: TimeSpan.FromMilliseconds(50));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a faulted rollup fold does not crash the actor and a subsequent tick still folds.</summary>
|
||||
[Fact]
|
||||
public void FaultedRollupFold_DoesNotCrashActor_AndSubsequentTickStillFolds()
|
||||
{
|
||||
@@ -886,9 +944,10 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
}
|
||||
|
||||
// =====================================================================
|
||||
// One-shot rollup backfill (Task 6)
|
||||
// One-shot rollup backfill
|
||||
// =====================================================================
|
||||
|
||||
/// <summary>Verifies that the backfill tick folds the full retention window once.</summary>
|
||||
[Fact]
|
||||
public void BackfillTick_FoldsFullRetentionWindow_Once()
|
||||
{
|
||||
@@ -950,6 +1009,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
Assert.Equal(foldedCount, repository.FoldWindows.Count);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the backfill slices the retention window into bounded day-long folds, oldest first.</summary>
|
||||
[Fact]
|
||||
public void Backfill_SlicesRetentionWindow_IntoBoundedDayFolds_OldestFirst()
|
||||
{
|
||||
@@ -996,6 +1056,8 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
interval: TimeSpan.FromMilliseconds(50));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a periodic rollup tick interleaves between backfill slices.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task PeriodicRollupTick_Interleaves_BetweenBackfillSlices()
|
||||
{
|
||||
@@ -1060,6 +1122,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
interval: TimeSpan.FromMilliseconds(50));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that a backfill in flight causes the periodic rollup tick to be skipped.</summary>
|
||||
[Fact]
|
||||
public void BackfillInFlight_SkipsPeriodicRollupTick()
|
||||
{
|
||||
@@ -1105,6 +1168,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
interval: TimeSpan.FromMilliseconds(50));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the backfill is skipped when rollups are already current.</summary>
|
||||
[Fact]
|
||||
public void Backfill_Skips_WhenRollupsAlreadyCurrent()
|
||||
{
|
||||
@@ -1147,6 +1211,7 @@ public class KpiHistoryRecorderActorTests : TestKit
|
||||
"the done-latch must make a second BackfillTick a no-op");
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the backfill shrinks to the watermark instead of the retention floor.</summary>
|
||||
[Fact]
|
||||
public void Backfill_ShrinksToWatermark_InsteadOfRetentionFloor()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user