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
@@ -48,6 +48,7 @@ public sealed class BundleImporterApplyTests : IDisposable
private readonly ServiceProvider _provider;
private readonly RecordingScriptArtifactChangeBus _artifactBus = new();
/// <summary>Initializes a new instance of the <c>BundleImporterApplyTests</c> class.</summary>
public BundleImporterApplyTests()
{
var services = new ServiceCollection();
@@ -82,11 +83,11 @@ public sealed class BundleImporterApplyTests : IDisposable
services.AddScoped<IExternalSystemRepository, ExternalSystemRepository>();
services.AddScoped<INotificationRepository, NotificationRepository>();
services.AddScoped<IInboundApiRepository, InboundApiRepository>();
// M8: DependencyResolver now injects ISiteRepository to walk the
// DependencyResolver now injects ISiteRepository to walk the
// site/data-connection/instance closure; register it or activation fails.
services.AddScoped<ISiteRepository, SiteRepository>();
services.AddScoped<IDeploymentManagerRepository, DeploymentManagerRepository>();
// M9-T32: FlatteningPipeline gained an ISharedSchemaRepository constructor
// FlatteningPipeline gained an ISharedSchemaRepository constructor
// dependency for schema-library $ref resolution; register the real repo so
// AddDeploymentManager()'s IFlatteningPipeline → FlatteningPipeline activates.
// SharedSchemaRepository only needs ScadaBridgeDbContext, which is already
@@ -95,11 +96,11 @@ public sealed class BundleImporterApplyTests : IDisposable
services.AddScoped<IAuditCorrelationContext, AuditCorrelationContext>();
services.AddScoped<IAuditService, AuditService>();
services.AddTransport();
// #05-T14: a recording bus so the post-commit ScriptArtifactsChanged
// A recording bus so the post-commit ScriptArtifactsChanged
// publish is observable. BundleImporter takes IScriptArtifactChangeBus?
// optionally, so this registration flows in via DI activation.
services.AddSingleton<IScriptArtifactChangeBus>(_artifactBus);
// #16 (M8 D2): the stale-instance probe is implemented in DeploymentManager
// The stale-instance probe is implemented in DeploymentManager
// and the flatten/hash primitives in TemplateEngine — register both so
// BundleImporter resolves a real IStaleInstanceProbe and ApplyAsync can
// compute StaleInstanceIds against genuine revision hashes.
@@ -109,6 +110,7 @@ public sealed class BundleImporterApplyTests : IDisposable
_provider = services.BuildServiceProvider();
}
/// <summary>Disposes the underlying service provider.</summary>
public void Dispose() => _provider.Dispose();
// ---- helpers ----
@@ -168,6 +170,8 @@ public sealed class BundleImporterApplyTests : IDisposable
// ---- tests ----
/// <summary>Verifies that applying a bundle adds new artifacts in a single transaction.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_adds_new_artifacts_in_single_transaction()
{
@@ -210,6 +214,8 @@ public sealed class BundleImporterApplyTests : IDisposable
Assert.NotEqual(Guid.Empty, result.BundleImportId);
}
/// <summary>Verifies that applying a bundle overwrites an existing artifact when the resolution is Overwrite.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_overwrites_artifact_when_resolution_is_Overwrite()
{
@@ -251,6 +257,8 @@ public sealed class BundleImporterApplyTests : IDisposable
Assert.Equal(1, result.Overwritten);
}
/// <summary>Verifies that applying a bundle leaves an existing artifact untouched when the resolution is Skip.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_skips_artifact_when_resolution_is_Skip()
{
@@ -287,6 +295,8 @@ public sealed class BundleImporterApplyTests : IDisposable
Assert.Equal(0, result.Overwritten);
}
/// <summary>Verifies that applying a bundle imports an artifact under a new name when the resolution is Rename.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_renames_artifact_when_resolution_is_Rename()
{
@@ -321,13 +331,15 @@ public sealed class BundleImporterApplyTests : IDisposable
Assert.Equal(1, result.Renamed);
}
/// <summary>Verifies that applying a bundle rolls back all changes when semantic validation fails.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_rolls_back_all_changes_when_semantic_validation_fails()
{
// Arrange: seed an ApiMethod whose script body calls MissingHelper().
// No SharedScript by that name exists in source or (after wipe) in the
// target. ApiMethod name-resolution findings are HARD errors (unlike
// template-script findings, which are advisory as of #05-T19), so this
// template-script findings, which are advisory), so this
// must reject the apply and roll everything back.
await using (var scope = _provider.CreateAsyncScope())
{
@@ -364,6 +376,8 @@ public sealed class BundleImporterApplyTests : IDisposable
Assert.Null(sessionStore.Get(sessionId));
}
/// <summary>Verifies that applying a bundle removes the import session on the success path as well.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_removes_session_on_success_path_too()
{
@@ -392,6 +406,8 @@ public sealed class BundleImporterApplyTests : IDisposable
Assert.Null(sessionStore.Get(sessionId));
}
/// <summary>Verifies that applying a bundle stamps the bundle import id on every audit row it emits.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_writes_BundleImportId_on_every_emitted_audit_row()
{
@@ -444,6 +460,8 @@ public sealed class BundleImporterApplyTests : IDisposable
}
}
/// <summary>Verifies that applying a bundle writes the BundleImported summary row inside the transaction.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_writes_BundleImported_summary_row_inside_transaction()
{
@@ -482,6 +500,8 @@ public sealed class BundleImporterApplyTests : IDisposable
}
}
/// <summary>Verifies that a failed apply writes the BundleImportFailed row outside the rolled-back transaction.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_writes_BundleImportFailed_outside_rolled_back_transaction()
{
@@ -521,6 +541,8 @@ public sealed class BundleImporterApplyTests : IDisposable
}
}
/// <summary>Verifies that overwriting a template synchronizes its attributes, alarms and scripts to the bundle.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_Overwrite_synchronises_attributes_alarms_and_scripts_to_bundle()
{
@@ -664,6 +686,8 @@ public sealed class BundleImporterApplyTests : IDisposable
Assert.Equal(1, result.Overwritten);
}
/// <summary>Verifies that overwriting an external system synchronizes its methods to the bundle.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_Overwrite_synchronises_external_system_methods_to_bundle()
{
@@ -789,10 +813,12 @@ public sealed class BundleImporterApplyTests : IDisposable
Assert.Equal(1, result.Overwritten);
}
/// <summary>Verifies that adding a new external system carries over its timeout seconds.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_adds_new_external_system_with_TimeoutSeconds()
{
// arch-review 2026-08-19 P7-1 (row-27 shape): TimeoutSeconds is the
// TimeoutSeconds shape: TimeoutSeconds is the
// per-system HTTP timeout ExternalSystemClient reads at call time — it must
// survive Add, not just the retry policy (T-002 above covers the sibling
// Methods-drop regression on Overwrite; this is the Add-path fidelity gap).
@@ -831,6 +857,8 @@ public sealed class BundleImporterApplyTests : IDisposable
}
}
/// <summary>Verifies that overwriting an external system restores its timeout seconds to the bundle value.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_Overwrite_restores_TimeoutSeconds_to_bundle_value()
{
@@ -876,9 +904,9 @@ public sealed class BundleImporterApplyTests : IDisposable
}
// ─────────────────────────────────────────────────────────────────────
// arch-review 2026-08-19 P7-1: old-bundle semantics for TimeoutSeconds. Hand-packs
// Old-bundle semantics for TimeoutSeconds. Hand-packs
// a BundleContentDto with an ExternalSystemDto whose TimeoutSeconds is left at its
// default (null) — the same in-memory shape a pre-P7-1 bundle deserializes to,
// default (null) — the same in-memory shape a pre-TimeoutSeconds bundle deserializes to,
// since the field is simply absent from that JSON text.
// ─────────────────────────────────────────────────────────────────────
private async Task<Guid> PackAndLoadExternalSystemBundleAsync(params ExternalSystemDto[] externalSystems)
@@ -916,6 +944,8 @@ public sealed class BundleImporterApplyTests : IDisposable
return session.SessionId;
}
/// <summary>Verifies that adding from an old bundle without timeout seconds uses the entity default.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_Add_with_old_bundle_missing_TimeoutSeconds_uses_entity_default()
{
@@ -940,6 +970,8 @@ public sealed class BundleImporterApplyTests : IDisposable
}
}
/// <summary>Verifies that overwriting from an old bundle without timeout seconds preserves the target's value.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_Overwrite_with_old_bundle_missing_TimeoutSeconds_preserves_target_value()
{
@@ -1022,6 +1054,8 @@ public sealed class BundleImporterApplyTests : IDisposable
return session.SessionId;
}
/// <summary>Verifies that applying a bundle creates a new SMS config with its secret decrypted.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_creates_new_sms_config_with_decrypted_secret()
{
@@ -1060,6 +1094,8 @@ public sealed class BundleImporterApplyTests : IDisposable
Assert.Equal(1, result.Added);
}
/// <summary>Verifies that applying a bundle overwrites an existing SMS config matched by account SID.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_overwrites_existing_sms_config_matched_by_account_sid()
{
@@ -1119,6 +1155,8 @@ public sealed class BundleImporterApplyTests : IDisposable
// NOT fail to parse, must NOT create any ApiKey rows, and must surface the
// ignored-key count on the result (so the operator knows to re-issue keys).
// ─────────────────────────────────────────────────────────────────────
/// <summary>Verifies that applying a bundle ignores legacy API keys without failing.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_ignores_legacy_api_keys_in_bundle_without_failing()
{
@@ -1214,6 +1252,8 @@ public sealed class BundleImporterApplyTests : IDisposable
// surfaces no ApiKey/key preview items, and DOES surface the ApiMethod
// preview item.
// ─────────────────────────────────────────────────────────────────────
/// <summary>Verifies that previewing a legacy bundle does not surface API key items.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task PreviewAsync_on_legacy_bundle_does_not_surface_key_items()
{
@@ -1290,7 +1330,7 @@ public sealed class BundleImporterApplyTests : IDisposable
}
// ─────────────────────────────────────────────────────────────────────
// #16 (M8 D2): StaleInstanceIds enumeration. Overwriting a template changes
// StaleInstanceIds enumeration. Overwriting a template changes
// its flattened-config hash, so deployed instances of that template drift
// from their DeployedConfigSnapshot.RevisionHash and must surface as stale
// in the import result. NotDeployed (incl. freshly-imported) instances are
@@ -1356,6 +1396,8 @@ public sealed class BundleImporterApplyTests : IDisposable
return site.Id;
}
/// <summary>Verifies that overwriting a template marks its deployed instance as stale.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_overwriting_template_marks_deployed_instance_stale()
{
@@ -1405,6 +1447,8 @@ public sealed class BundleImporterApplyTests : IDisposable
Assert.Contains(deployedInstanceId, result.StaleInstanceIds);
}
/// <summary>Verifies that overwriting a template does not mark instances of an unchanged template as stale.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_overwriting_template_does_not_mark_unchanged_template_instance_stale()
{
@@ -1461,6 +1505,8 @@ public sealed class BundleImporterApplyTests : IDisposable
Assert.DoesNotContain(valveInstanceId, result.StaleInstanceIds);
}
/// <summary>Verifies that a freshly imported NotDeployed instance is never reported as stale.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_freshly_imported_NotDeployed_instance_is_never_stale()
{
@@ -1545,6 +1591,7 @@ public sealed class BundleImporterApplyTests : IDisposable
/// depends entirely on a FRESH hash rather than on the leftover drift.
/// </para>
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_second_overwrite_of_same_template_still_marks_instance_stale()
{
@@ -1587,7 +1634,7 @@ public sealed class BundleImporterApplyTests : IDisposable
}
var instanceId = await SeedDeployedInstanceWithRealSnapshotAsync("Pump", "Pump-Deployed");
// Import #1 — Overwrite to shape A. Drifts the instance off its snapshot.
// First import — Overwrite to shape A. Drifts the instance off its snapshot.
ImportResult first;
await using (var scope = _provider.CreateAsyncScope())
{
@@ -1598,11 +1645,11 @@ public sealed class BundleImporterApplyTests : IDisposable
}
Assert.Contains(instanceId, first.StaleInstanceIds);
// The operator redeploys: the snapshot now matches the post-import-#1 config,
// so the instance is genuinely up to date going into import #2.
// The operator redeploys: the snapshot now matches the post-first-import config,
// so the instance is genuinely up to date going into the second import.
await RefreshDeployedSnapshotAsync(instanceId);
// Import #2 — Overwrite the SAME template to shape B. The instance drifts
// Second import — Overwrite the SAME template to shape B. The instance drifts
// again and must be reported again.
ImportResult second;
await using (var scope = _provider.CreateAsyncScope())
@@ -1639,8 +1686,10 @@ public sealed class BundleImporterApplyTests : IDisposable
await ctx.SaveChangesAsync();
}
// ============ #05-T14: post-commit ScriptArtifactsChanged publish ============
// ============ Post-commit ScriptArtifactsChanged publish ============
/// <summary>Verifies that applying a bundle publishes a script-artifacts-changed notification per kind after commit.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_publishes_ScriptArtifactsChanged_per_kind_after_commit()
{
@@ -1703,6 +1752,8 @@ public sealed class BundleImporterApplyTests : IDisposable
Assert.All(_artifactBus.Received, n => Assert.Equal("BundleImport", n.Source));
}
/// <summary>Verifies that applying a bundle publishes script-artifacts-changed notifications for Add resolutions.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_publishes_ScriptArtifactsChanged_for_Add_resolutions()
{
@@ -1758,6 +1809,8 @@ public sealed class BundleImporterApplyTests : IDisposable
Assert.Contains("DelmiaRecipeDownload", notification.Names); // FAILS today: Adds are filtered out
}
/// <summary>Verifies that a failed apply publishes no notifications.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ApplyAsync_failed_apply_publishes_nothing()
{
@@ -1809,10 +1862,13 @@ public sealed class BundleImporterApplyTests : IDisposable
/// <summary>Records every published notification for assertion.</summary>
private sealed class RecordingScriptArtifactChangeBus : IScriptArtifactChangeBus
{
/// <summary>Gets the notifications received by this bus.</summary>
public List<ScriptArtifactsChanged> Received { get; } = new();
/// <inheritdoc />
public void Publish(ScriptArtifactsChanged notification) => Received.Add(notification);
/// <inheritdoc />
public IDisposable Subscribe(Action<ScriptArtifactsChanged> handler) =>
throw new NotSupportedException("The recording bus does not support subscriptions.");
}