docs: backfill XML documentation across 756 files
v2-ci / build (push) Failing after 1m43s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped

Adds <summary>, <param>, <typeparam>, and <inheritdoc/> tags to public
members surfaced by commentchecker — resolves 5,847 of 5,869 issues
(99.6%) across three /fixdocs passes.
This commit is contained in:
Joseph Doherty
2026-05-28 08:10:17 -04:00
parent f9fc7dd2e1
commit 64e3fbe035
756 changed files with 9876 additions and 96 deletions
@@ -11,6 +11,7 @@ namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;
/// </summary>
public sealed class ClusterFormationTests
{
/// <summary>Verifies that two nodes form a 2-member cluster.</summary>
[Fact]
public async Task Two_nodes_form_a_2_member_cluster()
{
@@ -27,6 +28,7 @@ public sealed class ClusterFormationTests
aRoles.ShouldContain("driver");
}
/// <summary>Verifies that both nodes see each other as role members.</summary>
[Fact]
public async Task Both_nodes_see_each_other_as_role_members()
{
@@ -18,6 +18,7 @@ public sealed class DeployHappyPathTests
{
private static CancellationToken Ct => TestContext.Current.CancellationToken;
/// <summary>Verifies that StartDeployment seals after both nodes apply.</summary>
[Fact]
public async Task StartDeployment_seals_after_both_nodes_apply()
{
@@ -53,6 +54,7 @@ public sealed class DeployHappyPathTests
nodeStates.ShouldAllBe(s => s.Status == NodeDeploymentStatus.Applied);
}
/// <summary>Verifies that replaying dispatch to same revision is idempotent and a no-op.</summary>
[Fact]
public async Task Replaying_dispatch_to_same_revision_is_idempotent_no_op()
{
@@ -18,6 +18,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>
[Fact]
public async Task Stopping_node_b_shrinks_cluster_to_one_up_member()
{
@@ -32,6 +33,7 @@ public sealed class FailoverDuringDeployTests
.Count(m => m.Status == MemberStatus.Up).ShouldBe(1);
}
/// <summary>Verifies that a restarted node B rejoins the cluster on the same port.</summary>
[Fact]
public async Task Restarted_node_b_rejoins_cluster_on_same_port()
{
@@ -48,6 +50,7 @@ public sealed class FailoverDuringDeployTests
.Count(m => m.Status == MemberStatus.Up).ShouldBe(2);
}
/// <summary>Verifies that a deployment started with node B down seals with one-node state.</summary>
[Fact]
public async Task Deployment_started_with_node_b_down_seals_with_one_node_state()
{
@@ -16,6 +16,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>
[Fact]
public async Task GetDiagnostics_returns_snapshot_with_target_NodeId()
{
@@ -39,6 +40,7 @@ public sealed class FleetDiagnosticsRoundTripTests
snapshot.AsOfUtc.ShouldBeGreaterThan(DateTime.UtcNow.AddSeconds(-30));
}
/// <summary>Verifies that get diagnostics after deploy reports the current revision.</summary>
[Fact]
public async Task GetDiagnostics_after_deploy_reports_current_revision()
{
@@ -13,6 +13,7 @@ namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;
/// </summary>
public sealed class LdapOpcUaUserAuthenticatorTests
{
/// <summary>Verifies that successful LDAP authentication returns Allow result with user roles.</summary>
[Fact]
public async Task Authenticate_LDAP_success_returns_Allow_with_roles()
{
@@ -26,6 +27,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests
result.Roles.ShouldBe(new[] { "ConfigEditor" });
}
/// <summary>Verifies that LDAP authentication failure returns Deny result with error text.</summary>
[Fact]
public async Task Authenticate_LDAP_failure_returns_Deny_with_error_text()
{
@@ -38,6 +40,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests
result.Error.ShouldBe("Invalid username or password");
}
/// <summary>Verifies that LDAP exceptions are converted to backend error denial results.</summary>
[Fact]
public async Task Authenticate_LDAP_exception_returns_backend_error_denial()
{
@@ -51,6 +54,7 @@ public sealed class LdapOpcUaUserAuthenticatorTests
result.Error.ShouldContain("backend");
}
/// <summary>Verifies that authentication falls back to username when LDAP omits display name.</summary>
[Fact]
public async Task Authenticate_falls_back_to_username_when_LDAP_omits_display_name()
{
@@ -63,12 +67,23 @@ public sealed class LdapOpcUaUserAuthenticatorTests
result.DisplayName.ShouldBe("alice");
}
/// <summary>Test fake implementation of LDAP authentication service.</summary>
private sealed class FakeLdap : ILdapAuthService
{
private readonly Func<string, LdapAuthResult> _handler;
/// <summary>Initializes the fake with a fixed result that ignores the username.</summary>
/// <param name="fixed_">The result to return for any authentication attempt.</param>
public FakeLdap(LdapAuthResult fixed_) => _handler = _ => fixed_;
/// <summary>Initializes the fake with a handler function for custom results.</summary>
/// <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>
public Task<LdapAuthResult> AuthenticateAsync(string username, string password, CancellationToken ct = default)
=> Task.FromResult(_handler(username));
}
@@ -13,6 +13,7 @@ namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;
/// </summary>
public sealed class RoslynScriptedAlarmEvaluatorTests
{
/// <summary>Verifies evaluation of predicate returning true reports Active.</summary>
[Fact]
public void Evaluate_predicate_returning_true_reports_Active()
{
@@ -27,6 +28,7 @@ public sealed class RoslynScriptedAlarmEvaluatorTests
result.Active.ShouldBeTrue();
}
/// <summary>Verifies evaluation of predicate returning false reports Inactive.</summary>
[Fact]
public void Evaluate_predicate_returning_false_reports_Inactive()
{
@@ -41,6 +43,7 @@ public sealed class RoslynScriptedAlarmEvaluatorTests
result.Active.ShouldBeFalse();
}
/// <summary>Verifies compiled predicates are cached across calls.</summary>
[Fact]
public void Evaluate_caches_compiled_predicate_across_calls()
{
@@ -54,6 +57,7 @@ public sealed class RoslynScriptedAlarmEvaluatorTests
second.Active.ShouldBeFalse();
}
/// <summary>Verifies compile errors return Failure.</summary>
[Fact]
public void Evaluate_compile_error_returns_Failure()
{
@@ -65,6 +69,7 @@ public sealed class RoslynScriptedAlarmEvaluatorTests
result.Reason!.ShouldContain("compile");
}
/// <summary>Verifies predicate writing virtual tag returns Failure.</summary>
[Fact]
public void Evaluate_predicate_writing_virtual_tag_returns_Failure()
{
@@ -80,6 +85,7 @@ public sealed class RoslynScriptedAlarmEvaluatorTests
result.Reason!.ShouldContain("threw");
}
/// <summary>Verifies empty predicate returns Failure.</summary>
[Fact]
public void Evaluate_empty_predicate_returns_Failure()
{
@@ -88,6 +94,7 @@ public sealed class RoslynScriptedAlarmEvaluatorTests
sut.Evaluate("alarm-empty", "", new Dictionary<string, object?>()).Success.ShouldBeFalse();
}
/// <summary>Verifies evaluation after dispose returns Failure.</summary>
[Fact]
public void Evaluate_after_dispose_returns_Failure()
{
@@ -13,6 +13,7 @@ namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;
/// </summary>
public sealed class RoslynVirtualTagEvaluatorTests
{
/// <summary>Verifies that simple addition expression is evaluated correctly.</summary>
[Fact]
public void Evaluate_simple_addition_returns_summed_value()
{
@@ -27,6 +28,7 @@ public sealed class RoslynVirtualTagEvaluatorTests
result.Value.ShouldBe(42);
}
/// <summary>Verifies that compiled expressions are cached across multiple calls.</summary>
[Fact]
public void Evaluate_caches_compiled_expression_across_calls()
{
@@ -42,6 +44,7 @@ public sealed class RoslynVirtualTagEvaluatorTests
second.Value.ShouldBe(14);
}
/// <summary>Verifies that compile errors return Failure with a descriptive reason.</summary>
[Fact]
public void Evaluate_compile_error_returns_Failure_with_reason()
{
@@ -54,6 +57,7 @@ public sealed class RoslynVirtualTagEvaluatorTests
result.Reason.ShouldContain("compile");
}
/// <summary>Verifies that runtime exceptions return Failure with a descriptive reason.</summary>
[Fact]
public void Evaluate_runtime_exception_returns_Failure_with_reason()
{
@@ -69,6 +73,7 @@ public sealed class RoslynVirtualTagEvaluatorTests
result.Reason.ShouldContain("threw");
}
/// <summary>Verifies that empty expressions return Failure.</summary>
[Fact]
public void Evaluate_empty_expression_returns_Failure()
{
@@ -78,6 +83,7 @@ public sealed class RoslynVirtualTagEvaluatorTests
sut.Evaluate("vt-empty", " ", new Dictionary<string, object?>()).Success.ShouldBeFalse();
}
/// <summary>Verifies that evaluation after disposal returns Failure.</summary>
[Fact]
public void Evaluate_after_dispose_returns_Failure()
{
@@ -48,26 +48,35 @@ namespace ZB.MOM.WW.OtOpcUa.Host.IntegrationTests;
public sealed class TwoNodeClusterHarness : IAsyncDisposable
{
public const string TestRoles = "admin,driver";
/// <summary>Gets the shared database name for both cluster nodes.</summary>
public string SharedDbName { get; } = $"two-node-cluster-{Guid.NewGuid():N}";
/// <summary>Gets the harness mode configuration from environment variables.</summary>
public HarnessMode Mode { get; } = HarnessMode.FromEnvironment();
private string? _sqlDbName;
private string? _sqlConnString;
/// <summary>Gets the first web application node.</summary>
public WebApplication NodeA { get; private set; } = null!;
/// <summary>Gets the second web application node.</summary>
public WebApplication NodeB { get; private set; } = null!;
/// <summary>Gets the Akka port allocated for node A.</summary>
public int NodeAAkkaPort { get; private set; }
/// <summary>Gets the Akka port allocated for node B.</summary>
public int NodeBAkkaPort { get; private set; }
// Both nodes bind to 127.0.0.1 — ClusterRoleInfo + ConfigPublishCoordinator encode
// host:port into NodeId so the cluster membership stays distinct on different ports.
public const string LoopbackHost = "127.0.0.1";
/// <summary>Gets the Akka ActorSystem for node A.</summary>
public ActorSystem NodeASystem => NodeA.Services.GetRequiredService<ActorSystem>();
/// <summary>Gets the Akka ActorSystem for node B.</summary>
public ActorSystem NodeBSystem => NodeB.Services.GetRequiredService<ActorSystem>();
/// <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>
public static async Task<TwoNodeClusterHarness> StartAsync(TimeSpan? formationTimeout = null)
{
var harness = new TwoNodeClusterHarness();
@@ -110,6 +119,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable
/// Rebuilds node B on the same Akka port + same ConfigDb and waits for the cluster
/// 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>
public async Task RestartNodeBAsync(TimeSpan? formationTimeout = null)
{
NodeB = await BuildNodeAsync(this, NodeRole.Joiner);
@@ -124,6 +134,8 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable
/// Waits for node A's cluster view to reach <paramref name="expectedUpMembers"/> members in
/// <see cref="MemberStatus.Up"/>. Used for asserting shrink-after-stop or grow-after-restart.
/// </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>
public async Task WaitForClusterSizeAsync(int expectedUpMembers, TimeSpan timeout)
{
var deadline = DateTime.UtcNow + timeout;
@@ -265,6 +277,7 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable
return port;
}
/// <summary>Asynchronously disposes both nodes and cleans up the SQL database if used.</summary>
public async ValueTask DisposeAsync()
{
if (NodeB is not null) await NodeB.DisposeAsync();
@@ -279,6 +292,8 @@ public sealed class TwoNodeClusterHarness : IAsyncDisposable
/// <summary>Captures the env-var driven harness mode at construction time.</summary>
public sealed record HarnessMode(bool UseSqlServer, bool UseRealLdap)
{
/// <summary>Creates a HarnessMode from environment variable settings.</summary>
/// <returns>A HarnessMode configured from OTOPCUA_HARNESS_USE_SQL and OTOPCUA_HARNESS_USE_LDAP environment variables.</returns>
public static HarnessMode FromEnvironment() => new(
UseSqlServer: Environment.GetEnvironmentVariable("OTOPCUA_HARNESS_USE_SQL") == "1",
UseRealLdap: Environment.GetEnvironmentVariable("OTOPCUA_HARNESS_USE_LDAP") == "1");
@@ -286,6 +301,11 @@ 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>
public Task<LdapAuthResult> AuthenticateAsync(string username, string password, CancellationToken ct = default)
=> Task.FromResult(new LdapAuthResult(
Success: password == "valid-password",