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
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:
@@ -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()
|
||||
{
|
||||
|
||||
+15
@@ -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));
|
||||
}
|
||||
|
||||
+7
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user