docs: complete XML doc comments via fixdocs (2757 to 131 findings)

Add missing <returns>/<param>/<summary>/<typeparam> tags and clean up
misused inheritdoc across 481 files so the documented API surface is
complete. Documentation-only (zero code lines changed). The 131 remaining
findings are inheritdoc-style warnings deliberately left to preserve
hand-written implementation rationale (plan-decision notes, race-condition
explanations).
This commit is contained in:
Joseph Doherty
2026-06-03 12:34:34 -04:00
parent c6d9b20d9f
commit bd6c0b4d3d
481 changed files with 2550 additions and 1668 deletions
@@ -12,6 +12,7 @@ namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;
public sealed class ClusterFormationTests
{
/// <summary>Verifies that two nodes form a 2-member cluster.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Two_nodes_form_a_2_member_cluster()
{
@@ -29,6 +30,7 @@ public sealed class ClusterFormationTests
}
/// <summary>Verifies that both nodes see each other as role members.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Both_nodes_see_each_other_as_role_members()
{
@@ -19,6 +19,7 @@ public sealed class DeployHappyPathTests
private static CancellationToken Ct => TestContext.Current.CancellationToken;
/// <summary>Verifies that StartDeployment seals after both nodes apply.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task StartDeployment_seals_after_both_nodes_apply()
{
@@ -55,6 +56,7 @@ public sealed class DeployHappyPathTests
}
/// <summary>Verifies that replaying dispatch to same revision is idempotent and a no-op.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Replaying_dispatch_to_same_revision_is_idempotent_no_op()
{
@@ -17,6 +17,7 @@ public static class DockerFixtureAvailability
/// <param name="host">The host to probe.</param>
/// <param name="port">The TCP port to connect to.</param>
/// <param name="timeoutMs">Maximum time to wait in milliseconds; defaults to 500.</param>
/// <returns><see langword="true"/> if the TCP connection succeeded within the timeout; otherwise <see langword="false"/>.</returns>
public static bool IsReachable(string host, int port, int timeoutMs = 500)
{
try
@@ -43,6 +44,7 @@ public static class DockerFixtureAvailability
/// </summary>
/// <param name="endpoint">Endpoint in <c>host:port</c> format.</param>
/// <param name="timeoutMs">Maximum time to wait in milliseconds; defaults to 500.</param>
/// <returns><see langword="true"/> if the TCP connection succeeded within the timeout; otherwise <see langword="false"/>.</returns>
public static bool IsReachable(string endpoint, int timeoutMs = 500)
{
try
@@ -32,6 +32,7 @@ public sealed class DriverProbeRegistrationTests
"Historian.Wonderware",
];
/// <summary>Verifies that AddOtOpcUaDriverProbes registers a probe for every AdminUI driver type.</summary>
[Fact]
public void AddOtOpcUaDriverProbes_registers_a_probe_for_every_AdminUI_driver_type()
{
@@ -49,6 +50,7 @@ public sealed class DriverProbeRegistrationTests
byType.ContainsKey(key).ShouldBeTrue($"No IDriverProbe registered for AdminUI driver type '{key}'.");
}
/// <summary>Verifies that AddOtOpcUaDriverProbes is idempotent when called multiple times.</summary>
[Fact]
public void AddOtOpcUaDriverProbes_is_idempotent()
{
@@ -37,6 +37,7 @@ public sealed class DriverReconnectE2eTests
/// to a deployed driver, so no <c>DriverInstanceActor</c> will act on the DPS
/// broadcast — the test is validating the command ingestion and reply path only.</para>
/// </summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Reconnect_RoundTrip_ReturnsOk()
{
@@ -63,6 +64,7 @@ public sealed class DriverReconnectE2eTests
/// is also accepted (idempotent at the actor layer — the actor simply re-broadcasts
/// to DPS and writes another <c>ConfigEdit</c> row).
/// </summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Reconnect_IsIdempotent_SecondCallAlsoReturnsOk()
{
@@ -39,6 +39,7 @@ public sealed class DriverStatusHubE2eTests
/// to both the <see cref="IDriverStatusSnapshotStore"/> (via <c>Upsert</c>) and the
/// mock <see cref="IHubContext{DriverStatusHub}"/> (via <c>SendAsync</c>).
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task StatusHub_BridgeActor_ForwardsHealthChanged_ToStoreAndHub()
{
@@ -106,6 +107,7 @@ public sealed class DriverStatusHubE2eTests
/// for the same instance ID results in the store holding only the most recent state
/// (last-write-wins) and both hub push calls being made.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task StatusHub_BridgeActor_LastSnapshotWins_InStore()
{
@@ -45,6 +45,7 @@ public sealed class DriverTestConnectE2eTests
/// the <see cref="TestDriverConnectResult"/> reports <c>Ok = true</c> with a
/// sub-5 s latency. Skipped when the Docker fixture host is unreachable.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task TestConnect_Modbus_AgainstFixture_ReportsOk()
{
@@ -74,6 +75,7 @@ public sealed class DriverTestConnectE2eTests
/// containing a connection-refused indicator. Skipped when the host is unreachable
/// (even a refused connection requires the IP to be routable).
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task TestConnect_Modbus_AgainstWrongPort_ReportsFailure()
{
@@ -118,6 +120,7 @@ public sealed class DriverTestConnectE2eTests
/// <see cref="TestConnect_Modbus_AgainstFixture_ReportsOk"/> (which skips in dev).
/// This test does NOT require any Docker fixture and always runs.</para>
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task TestConnect_Modbus_AgainstBlackHole_ReportsTimeout()
{
@@ -19,6 +19,7 @@ public sealed class FailoverDuringDeployTests
private static CancellationToken Ct => TestContext.Current.CancellationToken;
/// <summary>Verifies that stopping node B shrinks the cluster to one up member.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Stopping_node_b_shrinks_cluster_to_one_up_member()
{
@@ -34,6 +35,7 @@ public sealed class FailoverDuringDeployTests
}
/// <summary>Verifies that a restarted node B rejoins the cluster on the same port.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Restarted_node_b_rejoins_cluster_on_same_port()
{
@@ -51,6 +53,7 @@ public sealed class FailoverDuringDeployTests
}
/// <summary>Verifies that a deployment started with node B down seals with one-node state.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task Deployment_started_with_node_b_down_seals_with_one_node_state()
{
@@ -17,6 +17,7 @@ public sealed class FleetDiagnosticsRoundTripTests
private static CancellationToken Ct => TestContext.Current.CancellationToken;
/// <summary>Verifies that get diagnostics returns a snapshot with the target node ID.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task GetDiagnostics_returns_snapshot_with_target_NodeId()
{
@@ -41,6 +42,7 @@ public sealed class FleetDiagnosticsRoundTripTests
}
/// <summary>Verifies that get diagnostics after deploy reports the current revision.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task GetDiagnostics_after_deploy_reports_current_revision()
{
@@ -19,6 +19,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests
{
/// <summary>On success the data-plane authenticator resolves roles via the mapper from the
/// returned Groups — not from the auth result's Roles field — and grants identity.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Authenticate_LDAP_success_resolves_roles_via_mapper_from_groups()
{
@@ -37,6 +38,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests
/// <summary>The DevStub pre-resolved roles (Administrator) survive the move to the mapper: they are
/// unioned with the mapper output so the dev grant still reaches the OPC UA session.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Authenticate_devstub_preresolved_roles_are_unioned_with_mapper()
{
@@ -54,6 +56,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests
/// <summary>A mapper fault (e.g. DB outage) must not deny an authenticated session — it falls
/// back to the pre-resolved roles, matching the login endpoint's behaviour.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Authenticate_mapper_fault_falls_back_to_preresolved_roles()
{
@@ -68,6 +71,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests
}
/// <summary>Verifies that LDAP authentication failure returns Deny result with error text.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Authenticate_LDAP_failure_returns_Deny_with_error_text()
{
@@ -82,6 +86,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests
}
/// <summary>Verifies that LDAP exceptions are converted to backend error denial results.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Authenticate_LDAP_exception_returns_backend_error_denial()
{
@@ -97,6 +102,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests
}
/// <summary>Verifies that authentication falls back to username when LDAP omits display name.</summary>
/// <returns>A task that represents the asynchronous test operation.</returns>
[Fact]
public async Task Authenticate_falls_back_to_username_when_LDAP_omits_display_name()
{
@@ -131,10 +137,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests
/// <param name="handler">The handler to invoke with the username to produce a result.</param>
public FakeLdap(Func<string, LdapAuthResult> handler) => _handler = handler;
/// <summary>Authenticates a user asynchronously via the handler function.</summary>
/// <param name="username">The username to authenticate.</param>
/// <param name="password">The password (ignored by the fake).</param>
/// <param name="ct">Cancellation token for the operation.</param>
/// <inheritdoc />
public Task<LdapAuthResult> AuthenticateAsync(string username, string password, CancellationToken ct = default)
=> Task.FromResult(_handler(username));
}
@@ -145,6 +148,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests
/// <summary>Maps groups to roles via the configured delegate; Scope is always null.</summary>
/// <param name="groups">The LDAP groups to map.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task that resolves to the group role mapping result.</returns>
public Task<GroupRoleMapping<string>> MapAsync(IReadOnlyList<string> groups, CancellationToken ct)
=> Task.FromResult(new GroupRoleMapping<string>(map(groups), Scope: null));
}
@@ -88,6 +88,8 @@ public sealed class ProdOverlayValidationTests
return configuration.GetSection(LdapOptions.SectionName).Get<LdapOptions>() ?? new LdapOptions();
}
/// <summary>Verifies that each production overlay declares the ldaps transport.</summary>
/// <param name="fileName">The overlay file name to validate.</param>
[Theory]
[InlineData("appsettings.admin.json")]
[InlineData("appsettings.driver.json")]
@@ -100,6 +102,8 @@ public sealed class ProdOverlayValidationTests
options.Transport.ShouldBe(LdapTransport.Ldaps);
}
/// <summary>Verifies that each production overlay passes startup validation.</summary>
/// <param name="fileName">The overlay file name to validate.</param>
[Theory]
[InlineData("appsettings.admin.json")]
[InlineData("appsettings.driver.json")]
@@ -114,6 +118,7 @@ public sealed class ProdOverlayValidationTests
Sut.Validate(null, options).Succeeded.ShouldBeTrue();
}
/// <summary>Verifies that the Development overlay passes startup validation via the DevStub exemption.</summary>
[Fact]
public void Development_overlay_passes_startup_validation_via_devstub_exemption()
{
@@ -77,6 +77,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable
/// <summary>Boots both nodes and waits up to <paramref name="formationTimeout"/> for cluster convergence.</summary>
/// <param name="formationTimeout">Maximum time to wait for cluster formation; defaults to 20 seconds if not provided.</param>
/// <returns>A task that resolves to the started two-node cluster harness.</returns>
public static async Task<TwoNodeClusterHarness> StartAsync(TimeSpan? formationTimeout = null)
{
var harness = new TwoNodeClusterHarness();
@@ -108,6 +109,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable
/// a couple of seconds. Use this for failover scenarios; call <see cref="RestartNodeBAsync"/>
/// to bring it back on the same Akka port.
/// </summary>
/// <returns>A task that represents the asynchronous stop operation.</returns>
public async Task StopNodeBAsync()
{
if (NodeB is null) return;
@@ -120,6 +122,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable
/// to re-converge to 2 Up members. Use after <see cref="StopNodeBAsync"/> to test rejoin.
/// </summary>
/// <param name="formationTimeout">The maximum time to wait for cluster formation; defaults to 20 seconds.</param>
/// <returns>A task that represents the asynchronous restart operation.</returns>
public async Task RestartNodeBAsync(TimeSpan? formationTimeout = null)
{
NodeB = await BuildNodeAsync(this, NodeRole.Joiner);
@@ -136,6 +139,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable
/// </summary>
/// <param name="expectedUpMembers">The expected number of Up members in the cluster.</param>
/// <param name="timeout">The maximum time to wait for the expected cluster size.</param>
/// <returns>A task that represents the asynchronous wait operation.</returns>
public async Task WaitForClusterSizeAsync(int expectedUpMembers, TimeSpan timeout)
{
var deadline = DateTime.UtcNow + timeout;
@@ -280,6 +284,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable
}
/// <summary>Asynchronously disposes both nodes and cleans up the SQL database if used.</summary>
/// <returns>A value task that represents the asynchronous dispose operation.</returns>
public async ValueTask DisposeAsync()
{
if (NodeB is not null) await NodeB.DisposeAsync();
@@ -303,11 +308,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable
private sealed class StubLdapAuthService : ILdapAuthService
{
/// <summary>Asynchronously authenticates a user with the stub LDAP service.</summary>
/// <param name="username">The username to authenticate.</param>
/// <param name="password">The password to authenticate against.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task that returns the LDAP authentication result.</returns>
/// <inheritdoc />
public Task<LdapAuthResult> AuthenticateAsync(string username, string password, CancellationToken ct = default)
=> Task.FromResult(new LdapAuthResult(
Success: password == "valid-password",