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:
Joseph Doherty
2026-10-07 15:05:49 -04:00
parent 0ffd4bdbea
commit 513fa1aa16
998 changed files with 17896 additions and 2395 deletions
@@ -8,7 +8,7 @@ using ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Repositories;
namespace ZB.MOM.WW.ScadaBridge.ConfigurationDatabase.Tests.Repositories;
/// <summary>
/// M6 (K2) coverage for <see cref="KpiHistoryRepository"/> over the in-memory SQLite
/// Coverage for <see cref="KpiHistoryRepository"/> over the in-memory SQLite
/// harness. Exercises the bulk write, the single-series query (including the
/// null-ScopeKey Global match), and the retention purge.
/// </summary>
@@ -31,6 +31,8 @@ public class KpiHistoryRepositoryTests
CapturedAtUtc = capturedAtUtc,
};
/// <summary>Verifies that the raw series query returns only the matching series in ascending time order.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task GetRawSeriesAsync_ReturnsOnlyMatchingSeries_InAscendingTimeOrder()
{
@@ -64,6 +66,8 @@ public class KpiHistoryRepositoryTests
Assert.Equal(7, series[1].Value);
}
/// <summary>Verifies that a site-scoped raw series query matches only rows with that scope key.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task GetRawSeriesAsync_SiteScopedKey_MatchesOnlyThatKey()
{
@@ -87,6 +91,8 @@ public class KpiHistoryRepositoryTests
Assert.Equal(5, series[0].Value);
}
/// <summary>Verifies that the raw series query honors the time window bounds.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task GetRawSeriesAsync_HonorsTimeWindowBounds()
{
@@ -109,6 +115,8 @@ public class KpiHistoryRepositoryTests
Assert.Equal(new[] { 1d, 2d }, series.Select(p => p.Value).ToArray());
}
/// <summary>Verifies that recording an empty batch is a no-op and querying an empty table returns an empty series.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task RecordSamplesAsync_EmptyBatch_IsNoOp_AndGetRawSeriesAsync_EmptyTable_ReturnsEmpty()
{
@@ -127,6 +135,8 @@ public class KpiHistoryRepositoryTests
Assert.Empty(series);
}
/// <summary>Verifies that the purge deletes only rows older than the cutoff and returns the deleted count.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task PurgeOlderThanAsync_DeletesOnlyRowsOlderThanCutoff_AndReturnsCount()
{
@@ -158,6 +168,8 @@ public class KpiHistoryRepositoryTests
Assert.Equal(new[] { 3d, 4d }, remaining.Select(p => p.Value).ToArray());
}
/// <summary>Verifies that a multi-window purge issues more than one delete and returns the total deleted count.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task PurgeOlderThanAsync_MultiWindow_IssuesMoreThanOneDelete_AndReturnsTotal()
{
@@ -196,8 +208,10 @@ public class KpiHistoryRepositoryTests
Assert.Equal(new[] { 4d, 5d }, remaining.Select(p => p.Value).ToArray());
}
// ---- Hourly rollup fold / query / purge (plan #22 T3) ------------------------------
// ---- Hourly rollup fold / query / purge ------------------------------
/// <summary>Verifies that a gauge metric folds to its last value per hour.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task FoldHourlyRollupsAsync_GaugeMetric_FoldsToLastValuePerHour()
{
@@ -224,6 +238,8 @@ public class KpiHistoryRepositoryTests
Assert.Equal(9, row.Value); // last-value (max CapturedAtUtc)
}
/// <summary>Verifies that a rate metric folds to its sum per hour along with the minimum, maximum and count.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task FoldHourlyRollupsAsync_RateMetric_FoldsToSumPerHour_WithMinMaxCount()
{
@@ -249,6 +265,8 @@ public class KpiHistoryRepositoryTests
Assert.Equal(3, rollup.SampleCount);
}
/// <summary>Verifies that the fold splits global-scope samples into separate hour buckets.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task FoldHourlyRollupsAsync_SplitsIntoSeparateHourBuckets_ForGlobalScope()
{
@@ -277,6 +295,8 @@ public class KpiHistoryRepositoryTests
Assert.Equal(2, series[1].Value); // hour 1 last-value
}
/// <summary>Verifies that the fold excludes an incomplete hour at the exclusive upper bound.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task FoldHourlyRollupsAsync_ExcludesIncompleteHour_AtExclusiveUpperBound()
{
@@ -303,6 +323,8 @@ public class KpiHistoryRepositoryTests
Assert.Equal(3, row.Value);
}
/// <summary>Verifies that repeated folds over the same window are idempotent.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task FoldHourlyRollupsAsync_IsIdempotent_AcrossRepeatedRunsOverSameWindow()
{
@@ -325,10 +347,12 @@ public class KpiHistoryRepositoryTests
Assert.Equal(2, row.SampleCount);
}
/// <summary>Verifies that the fold preloads existing rollups instead of probing once per series and hour.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task FoldHourlyRollupsAsync_PreloadsExistingRollups_WithoutAPerSeriesHourProbe()
{
// WP2.2: the fold used to issue one FirstOrDefaultAsync existence probe per
// The fold used to issue one FirstOrDefaultAsync existence probe per
// (series, hour) group before writing anything — an N+1 that scaled with
// the metric catalogue times the lookback window. It now preloads the whole
// window in a single query and resolves each group from a dictionary.
@@ -376,6 +400,8 @@ public class KpiHistoryRepositoryTests
Assert.All(rollups, r => Assert.Equal(1, r.SampleCount));
}
/// <summary>Verifies that the hourly series query returns rows in ascending order and distinguishes a null scope key from a site scope key.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task GetHourlySeriesAsync_ReturnsAscending_AndHonorsNullVsSiteScopeKey()
{
@@ -409,6 +435,8 @@ public class KpiHistoryRepositoryTests
Assert.Equal(42, Assert.Single(site).Value);
}
/// <summary>Verifies that the rollup purge deletes only rollups older than the cutoff.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task PurgeRollupsOlderThanAsync_DeletesOnlyRollupsOlderThanCutoff()
{
@@ -437,6 +465,8 @@ public class KpiHistoryRepositoryTests
Assert.Equal(new[] { 3d, 4d }, remaining.Select(p => p.Value).ToArray());
}
/// <summary>Verifies that the fold does not leave the sample entities tracked by the change tracker.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task FoldHourlyRollups_DoesNotTrack_KpiSampleEntities()
{
@@ -457,6 +487,8 @@ public class KpiHistoryRepositoryTests
Assert.Empty(ctx.ChangeTracker.Entries<KpiSample>());
}
/// <summary>Verifies that the latest rollup hour is null when no rollups exist.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task GetLatestRollupHour_ReturnsNull_WhenNoRollupsExist()
{
@@ -465,6 +497,8 @@ public class KpiHistoryRepositoryTests
Assert.Null(await repo.GetLatestRollupHourAsync());
}
/// <summary>Verifies that the latest rollup hour is the newest hour start across all series.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task GetLatestRollupHour_ReturnsNewestHourStart_AcrossAllSeries()
{
@@ -480,8 +514,10 @@ public class KpiHistoryRepositoryTests
Assert.Equal(Base.AddHours(3), await repo.GetLatestRollupHourAsync());
}
// ---- Task 9: failover fold-race failure grain is the pass, not the row (R5) ----
// ---- Failover fold-race failure grain is the pass, not the row (R5) ----
/// <summary>Verifies that a fold that loses a failover upsert race does not throw and the next fold self-heals.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Fold_LosingFailoverUpsertRace_DoesNotThrow_AndNextFoldSelfHeals()
{
@@ -556,12 +592,16 @@ public class KpiHistoryRepositoryTests
private readonly Func<KpiRollupHourly> _rowFactory;
private bool _fired;
/// <summary>Initializes the interceptor.</summary>
/// <param name="connection">The shared database connection used for the conflicting side write.</param>
/// <param name="rowFactory">Creates the conflicting rollup row to insert.</param>
public InsertConflictingRollupOnFirstSave(DbConnection connection, Func<KpiRollupHourly> rowFactory)
{
_connection = connection;
_rowFactory = rowFactory;
}
/// <inheritdoc />
public override async ValueTask<InterceptionResult<int>> SavingChangesAsync(
DbContextEventData eventData, InterceptionResult<int> result,
CancellationToken cancellationToken = default)
@@ -584,13 +624,6 @@ public class KpiHistoryRepositoryTests
}
}
/// <summary>
/// EF command interceptor that counts the DELETE statements actually issued
/// to the store, so a test can prove the purge is sliced into multiple
/// windowed DELETEs rather than one mega-statement. Threads both the sync and
/// async non-query entry points (<c>ExecuteDeleteAsync</c> routes through the
/// async path).
/// </summary>
/// <summary>
/// Counts reader commands that SELECT from <c>KpiRollupHourly</c>, so a test
/// can prove the hourly fold resolves existing rows from a single preload
@@ -598,8 +631,10 @@ public class KpiHistoryRepositoryTests
/// </summary>
private sealed class RollupSelectCountingInterceptor : DbCommandInterceptor
{
/// <summary>Number of reader commands that selected from the rollup table.</summary>
public int RollupSelectCount { get; private set; }
/// <summary>Resets the select counter to zero.</summary>
public void Reset() => RollupSelectCount = 0;
private void CountIfRollupSelect(DbCommand command)
@@ -611,6 +646,7 @@ public class KpiHistoryRepositoryTests
}
}
/// <inheritdoc />
public override InterceptionResult<DbDataReader> ReaderExecuting(
DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
@@ -618,6 +654,7 @@ public class KpiHistoryRepositoryTests
return base.ReaderExecuting(command, eventData, result);
}
/// <inheritdoc />
public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(
DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result,
CancellationToken cancellationToken = default)
@@ -627,10 +664,19 @@ public class KpiHistoryRepositoryTests
}
}
/// <summary>
/// EF command interceptor that counts the DELETE statements actually issued
/// to the store, so a test can prove the purge is sliced into multiple
/// windowed DELETEs rather than one mega-statement. Threads both the sync and
/// async non-query entry points (<c>ExecuteDeleteAsync</c> routes through the
/// async path).
/// </summary>
private sealed class DeleteCountingInterceptor : DbCommandInterceptor
{
/// <summary>Number of DELETE statements issued so far.</summary>
public int DeleteCount { get; private set; }
/// <summary>Resets the delete counter to zero.</summary>
public void Reset() => DeleteCount = 0;
private void CountIfDelete(DbCommand command)
@@ -641,6 +687,7 @@ public class KpiHistoryRepositoryTests
}
}
/// <inheritdoc />
public override InterceptionResult<int> NonQueryExecuting(
DbCommand command, CommandEventData eventData, InterceptionResult<int> result)
{
@@ -648,6 +695,7 @@ public class KpiHistoryRepositoryTests
return base.NonQueryExecuting(command, eventData, result);
}
/// <inheritdoc />
public override ValueTask<InterceptionResult<int>> NonQueryExecutingAsync(
DbCommand command, CommandEventData eventData, InterceptionResult<int> result,
CancellationToken cancellationToken = default)