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
@@ -25,6 +25,8 @@ namespace ZB.MOM.WW.ScadaBridge.IntegrationTests.Cluster;
/// </summary>
public class ActiveNodeSemanticsTests
{
/// <summary>Verifies that after the oldest node restarts, the surviving node stays active and the rejoining node becomes standby.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task AfterOldestNodeRestarts_SurvivorStaysActive_RejoinerIsStandby()
{
@@ -10,7 +10,7 @@ using ZB.MOM.WW.ScadaBridge.Host.Actors;
namespace ZB.MOM.WW.ScadaBridge.IntegrationTests.Cluster;
/// <summary>
/// Real-ActorSystem tests for the simultaneous-cold-start split-brain guard (Gitea #33):
/// Real-ActorSystem tests for the simultaneous-cold-start split-brain guard:
/// <see cref="ClusterBootstrapCoordinator"/> + <see cref="ClusterBootstrapGuard"/>, driven exactly as
/// production drives them — the node's ActorSystem is built from the production <c>BuildHocon</c> output
/// with the guard enabled (which emits an EMPTY seed list, so Akka does not auto-join), then the real
@@ -82,6 +82,7 @@ public sealed class ClusterBootstrapCoordinatorTests : IAsyncLifetime
/// The FOUNDER (lower address) with the guard on forms a cluster alone when its partner is dead —
/// exactly as un-guarded self-first does. The guard must not regress the preferred founder.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Founder_forms_alone_when_partner_is_dead()
{
@@ -102,6 +103,7 @@ public sealed class ClusterBootstrapCoordinatorTests : IAsyncLifetime
/// If the guard's peer-first logic were wrong, this node would hang in JoinSeedNodeProcess forever —
/// the very regression a naive "always let the lower node found" rule would introduce.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Higher_node_forms_alone_when_partner_is_dead_after_probing()
{
@@ -122,6 +124,7 @@ public sealed class ClusterBootstrapCoordinatorTests : IAsyncLifetime
/// window — it is genuinely waiting/probing, not self-forming immediately (which would mean the
/// guard is inert). If this ever starts seeing a member before the window, the probe is not gating.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Higher_node_does_not_form_before_the_probe_window_expires()
{
@@ -142,6 +145,7 @@ public sealed class ClusterBootstrapCoordinatorTests : IAsyncLifetime
/// then the higher node — its probe finds the founder reachable, it commits peer-first, and the
/// pair converges to ONE cluster of two.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Higher_node_joins_a_live_founder()
{
@@ -163,11 +167,12 @@ public sealed class ClusterBootstrapCoordinatorTests : IAsyncLifetime
}
/// <summary>
/// THE HEADLINE #33 CASE. Both nodes cold-start truly simultaneously with the guard on. Without the
/// THE HEADLINE CASE. Both nodes cold-start truly simultaneously with the guard on. Without the
/// guard each would run FirstSeedNodeProcess, race, and form its own 1-node cluster (split brain).
/// With it, the founder forms and the higher node probes-then-joins, so the pair converges to
/// EXACTLY ONE cluster of two — no two oldest, no dual singletons.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Both_nodes_cold_starting_together_form_exactly_one_cluster()
{
@@ -188,8 +193,12 @@ public sealed class ClusterBootstrapCoordinatorTests : IAsyncLifetime
"the higher node must be in the SAME cluster as the founder");
}
/// <summary>Prepares the fixture before the tests run.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
public Task InitializeAsync() => Task.CompletedTask;
/// <summary>Releases the resources held by this fixture asynchronously.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task DisposeAsync()
{
foreach (var c in _coordinators)
@@ -45,6 +45,8 @@ public sealed class ManualFailoverTests : IAsyncDisposable
return system;
}
/// <summary>Verifies that failover, makes, the, oldest, leave, and, the, survivor, take, over.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Failover_makes_the_oldest_leave_and_the_survivor_take_over()
{
@@ -66,6 +68,8 @@ public sealed class ManualFailoverTests : IAsyncDisposable
Assert.True(ActiveNodeEvaluator.SelfIsOldestUp(Akka.Cluster.Cluster.Get(f.NodeB), "Central"));
}
/// <summary>Verifies that failover, refuses, when, no, peer, exists.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Failover_refuses_when_no_peer_exists()
{
@@ -82,6 +86,8 @@ public sealed class ManualFailoverTests : IAsyncDisposable
Assert.True(ActiveNodeEvaluator.SelfIsOldestUp(Akka.Cluster.Cluster.Get(solo), "Central"));
}
/// <summary>Verifies that failover, dry, run, names, the, target, without, moving, the, cluster.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Failover_dry_run_names_the_target_without_moving_the_cluster()
{
@@ -98,6 +104,8 @@ public sealed class ManualFailoverTests : IAsyncDisposable
Assert.True(ActiveNodeEvaluator.SelfIsOldestUp(oldest, "Central"));
}
/// <summary>Terminates the actor systems and disposes the database environment.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
public async ValueTask DisposeAsync()
{
foreach (var s in _systems)
@@ -18,7 +18,7 @@ namespace ZB.MOM.WW.ScadaBridge.IntegrationTests.Cluster;
/// <item><c>keep-oldest</c> — downs the side that does NOT contain the oldest member,
/// and its <c>down-if-alone</c> escape only fires when the surviving side has ≥2
/// members (<c>KeepOldest.OldestDecision</c>: <c>otherSide == 1 &amp;&amp; thisSide &gt;= 2</c>).
/// With 1-vs-1 the younger survivor therefore takes <c>DownReachable</c> — it downs
/// With a two-node cluster the younger survivor therefore takes <c>DownReachable</c> — it downs
/// ITSELF — so only a YOUNGER-node crash is survivable.</item>
/// <item><c>auto-down</c> (production default, decision 2026-07-21) — the leader among
/// the reachable members downs the unreachable peer after the stability window, so a
@@ -30,6 +30,7 @@ public class SbrFailoverTests
{
private sealed class EchoActor : ReceiveActor
{
/// <summary>Initializes the actor to echo every message back to its sender.</summary>
public EchoActor() => ReceiveAny(msg => Sender.Tell(msg));
}
@@ -47,6 +48,8 @@ public class SbrFailoverTests
return (manager, proxy);
}
/// <summary>Verifies that when the younger node crashes hard, the split brain resolver downs it and the oldest node keeps the singleton.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task HardCrashOfYoungerNode_SbrDownsIt_AndOldestKeepsSingleton()
{
@@ -84,6 +87,8 @@ public class SbrFailoverTests
throw new Xunit.Sdk.XunitException($"Singleton stopped answering on the surviving oldest node after SBR downing: {last}");
}
/// <summary>Verifies that with auto-down, a hard crash of the oldest node lets the younger survivor take over the singleton.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task AutoDown_HardCrashOfOldestNode_YoungerSurvivorTakesOverSingleton()
{
@@ -128,6 +133,8 @@ public class SbrFailoverTests
$"Singleton never migrated to the younger survivor after the oldest crashed under auto-down: {last}");
}
/// <summary>Verifies that with auto-down, a hard crash of the younger node leaves the singleton on the oldest node.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task AutoDown_HardCrashOfYoungerNode_OldestKeepsSingleton()
{
@@ -68,6 +68,8 @@ public sealed class SelfFirstSeedBootstrapTests : IAsyncLifetime
return system;
}
/// <summary>Verifies that a lone node cold-starting with a dead peer forms a cluster on its own.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Lone_cold_start_forms_a_cluster_when_the_peer_is_dead()
{
@@ -81,6 +83,8 @@ public sealed class SelfFirstSeedBootstrapTests : IAsyncLifetime
Assert.Equal(MemberStatus.Up, Akka.Cluster.Cluster.Get(node).SelfMember.Status);
}
/// <summary>Verifies that listing the peer first in the seed nodes is the outage gap, so the lone node never forms a cluster.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Peer_first_ordering_is_the_outage_gap_and_never_forms()
{
@@ -100,6 +104,8 @@ public sealed class SelfFirstSeedBootstrapTests : IAsyncLifetime
await TwoNodeClusterFixture.WaitForMembersUp(node, 1, TimeSpan.FromSeconds(30));
}
/// <summary>Verifies that a restarting node rejoins its live peer instead of forming its own cluster.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Restarting_node_rejoins_its_live_peer_instead_of_self_forming()
{
@@ -123,6 +129,8 @@ public sealed class SelfFirstSeedBootstrapTests : IAsyncLifetime
await TwoNodeClusterFixture.WaitForMembersUp(nodeA, 2, TimeSpan.FromSeconds(90));
}
/// <summary>Verifies that two nodes cold-starting together converge on a single cluster.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Both_nodes_cold_starting_together_converge_on_one_cluster()
{
@@ -140,8 +148,12 @@ public sealed class SelfFirstSeedBootstrapTests : IAsyncLifetime
await TwoNodeClusterFixture.WaitForMembersUp(nodeB, 2, TimeSpan.FromSeconds(60));
}
/// <summary>Prepares the fixture before the tests run.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
public Task InitializeAsync() => Task.CompletedTask;
/// <summary>Releases the resources held by this fixture asynchronously.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
public async Task DisposeAsync()
{
foreach (var s in _systems)
@@ -5,7 +5,7 @@ using ZB.MOM.WW.ScadaBridge.Communication.ClusterState;
namespace ZB.MOM.WW.ScadaBridge.IntegrationTests.Cluster;
/// <summary>
/// Real-cluster proof for the central→site failover relay (Task 10). The unit tests in
/// Real-cluster proof for the central→site failover relay. The unit tests in
/// <c>SiteCommunicationActorTests</c> pin the routing and guard logic with the failover action
/// stubbed out; this pins the part they cannot — that the shared
/// <see cref="ClusterFailoverCoordinator"/> actually moves a SITE pair when scoped to the
@@ -17,6 +17,8 @@ namespace ZB.MOM.WW.ScadaBridge.IntegrationTests.Cluster;
/// </summary>
public sealed class SiteFailoverRelayTests
{
/// <summary>Verifies that failing over a site pair moves the oldest node and the survivor takes over.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Failing_over_a_site_pair_moves_the_oldest_and_the_survivor_takes_over()
{
@@ -35,6 +37,8 @@ public sealed class SiteFailoverRelayTests
Assert.True(ActiveNodeEvaluator.SelfIsOldestUp(Akka.Cluster.Cluster.Get(f.NodeB), "site-SiteA"));
}
/// <summary>Verifies that a site role scope matching no member is refused.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task A_site_role_scope_that_matches_no_member_is_refused()
{
@@ -20,11 +20,24 @@ namespace ZB.MOM.WW.ScadaBridge.IntegrationTests.Cluster;
/// </summary>
public sealed class TwoNodeClusterFixture : IAsyncDisposable
{
/// <summary>Gets the first cluster node, which is always started first and is therefore the oldest member.</summary>
public ActorSystem NodeA { get; private set; } = null!;
/// <summary>Gets the second cluster node, which joins after the first.</summary>
public ActorSystem NodeB { get; private set; } = null!;
/// <summary>Gets the remoting port used by the first node.</summary>
public int PortA { get; private set; }
/// <summary>Gets the remoting port used by the second node.</summary>
public int PortB { get; private set; }
/// <summary>Starts both nodes in order and waits until each sees two Up members.</summary>
/// <param name="role">The Akka role assigned to both nodes.</param>
/// <param name="stableAfter">How long membership must be stable before downing decisions; defaults to a scaled test value.</param>
/// <param name="portA">The remoting port for the first node, or null to pick a free port.</param>
/// <param name="portB">The remoting port for the second node, or null to pick a free port.</param>
/// <param name="heartbeatInterval">The failure-detector heartbeat interval; defaults to a scaled test value.</param>
/// <param name="failureDetectionThreshold">The failure-detector threshold; defaults to a scaled test value.</param>
/// <param name="strategy">The split-brain resolver strategy to configure.</param>
/// <returns>A task that yields the started two-node fixture.</returns>
public static async Task<TwoNodeClusterFixture> StartAsync(
string role = "Central", TimeSpan? stableAfter = null,
int? portA = null, int? portB = null,
@@ -45,6 +58,13 @@ public sealed class TwoNodeClusterFixture : IAsyncDisposable
/// <summary>Starts a node from production HOCON; used by StartAsync and by restart-scenarios.
/// <paramref name="strategy"/> defaults to the production posture (auto-down, decision
/// 2026-07-21); pass "keep-oldest" to exercise the legacy SBR path.</summary>
/// <param name="port">The remoting port for the node.</param>
/// <param name="role">The Akka role assigned to the node.</param>
/// <param name="stableAfter">How long membership must be stable before downing decisions; defaults to a scaled test value.</param>
/// <param name="heartbeatInterval">The failure-detector heartbeat interval; defaults to a scaled test value.</param>
/// <param name="failureDetectionThreshold">The failure-detector threshold; defaults to a scaled test value.</param>
/// <param name="strategy">The split-brain resolver strategy to configure.</param>
/// <returns>The started actor system.</returns>
public ActorSystem StartNode(int port, string role, TimeSpan? stableAfter = null,
TimeSpan? heartbeatInterval = null, TimeSpan? failureDetectionThreshold = null,
string strategy = "auto-down")
@@ -76,8 +96,15 @@ public sealed class TwoNodeClusterFixture : IAsyncDisposable
}
/// <summary>Hard-crash a node: abrupt terminate, no cluster leave.</summary>
/// <param name="node">The node to crash.</param>
/// <returns>A task that represents the asynchronous termination.</returns>
public static Task CrashNode(ActorSystem node) => node.Terminate();
/// <summary>Waits until the given system sees exactly the given number of Up members and none unreachable.</summary>
/// <param name="system">The actor system whose cluster view is polled.</param>
/// <param name="count">The expected number of Up members.</param>
/// <param name="timeout">The maximum time to wait.</param>
/// <returns>A task that completes when the expected membership is reached, or throws <see cref="TimeoutException"/> on timeout.</returns>
public static async Task WaitForMembersUp(ActorSystem system, int count, TimeSpan timeout)
{
var cluster = Akka.Cluster.Cluster.Get(system);
@@ -95,6 +122,11 @@ public sealed class TwoNodeClusterFixture : IAsyncDisposable
$"Unreachable: [{string.Join(", ", cluster.State.Unreachable)}]");
}
/// <summary>Waits until the given address is neither a member nor unreachable in the survivor's cluster view.</summary>
/// <param name="survivor">The actor system whose cluster view is polled.</param>
/// <param name="removed">The address expected to disappear from the view.</param>
/// <param name="timeout">The maximum time to wait.</param>
/// <returns>A task that completes when the member is gone, or throws <see cref="TimeoutException"/> on timeout.</returns>
public static async Task WaitForMemberRemoved(ActorSystem survivor, Address removed, TimeSpan timeout)
{
var cluster = Akka.Cluster.Cluster.Get(survivor);
@@ -109,6 +141,8 @@ public sealed class TwoNodeClusterFixture : IAsyncDisposable
throw new TimeoutException($"Member {removed} was never removed from {cluster.SelfAddress}'s view — SBR did not down it.");
}
/// <summary>Finds a free loopback TCP port.</summary>
/// <returns>A port number that was free when probed.</returns>
public static int GetFreeTcpPort()
{
var listener = new System.Net.Sockets.TcpListener(System.Net.IPAddress.Loopback, 0);
@@ -118,6 +152,8 @@ public sealed class TwoNodeClusterFixture : IAsyncDisposable
return port;
}
/// <summary>Releases the resources used by this instance asynchronously.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
public async ValueTask DisposeAsync()
{
foreach (var sys in new[] { NodeA, NodeB })
@@ -4,6 +4,8 @@ namespace ZB.MOM.WW.ScadaBridge.IntegrationTests.Cluster;
public class TwoNodeClusterFixtureTests
{
/// <summary>Verifies that the fixture forms a two-node cluster in which node A is the oldest member.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Fixture_FormsTwoNodeCluster_NodeAIsOldest()
{