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
@@ -7,6 +7,9 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
[Trait("Category", "Unit")]
public sealed class OpcUaClientAlarmTests
{
/// <summary>Verifies that MapSeverity buckets per OPC UA Part 9 guidance.</summary>
/// <param name="opcSev">The OPC UA severity value (11000).</param>
/// <param name="expected">The expected mapped alarm severity.</param>
[Theory]
[InlineData((ushort)1, AlarmSeverity.Low)]
[InlineData((ushort)200, AlarmSeverity.Low)]
@@ -21,6 +24,7 @@ public sealed class OpcUaClientAlarmTests
OpcUaClientDriver.MapSeverity(opcSev).ShouldBe(expected);
}
/// <summary>Verifies that MapSeverity zero maps to Low.</summary>
[Fact]
public void MapSeverity_zero_maps_to_Low()
{
@@ -28,6 +32,7 @@ public sealed class OpcUaClientAlarmTests
OpcUaClientDriver.MapSeverity(0).ShouldBe(AlarmSeverity.Low);
}
/// <summary>Verifies that SubscribeAlarmsAsync without initialize throws InvalidOperationException.</summary>
[Fact]
public async Task SubscribeAlarmsAsync_without_initialize_throws_InvalidOperationException()
{
@@ -36,6 +41,7 @@ public sealed class OpcUaClientAlarmTests
await drv.SubscribeAlarmsAsync([], TestContext.Current.CancellationToken));
}
/// <summary>Verifies that UnsubscribeAlarmsAsync with unknown handle is noop.</summary>
[Fact]
public async Task UnsubscribeAlarmsAsync_with_unknown_handle_is_noop()
{
@@ -44,6 +50,7 @@ public sealed class OpcUaClientAlarmTests
await drv.UnsubscribeAlarmsAsync(new FakeAlarmHandle(), TestContext.Current.CancellationToken);
}
/// <summary>Verifies that AcknowledgeAsync without initialize throws InvalidOperationException.</summary>
[Fact]
public async Task AcknowledgeAsync_without_initialize_throws_InvalidOperationException()
{
@@ -54,6 +61,7 @@ public sealed class OpcUaClientAlarmTests
TestContext.Current.CancellationToken));
}
/// <summary>Verifies that AcknowledgeAsync with empty batch is noop even without init.</summary>
[Fact]
public async Task AcknowledgeAsync_with_empty_batch_is_noop_even_without_init()
{
@@ -65,6 +73,7 @@ public sealed class OpcUaClientAlarmTests
private sealed class FakeAlarmHandle : IAlarmSubscriptionHandle
{
/// <summary>Gets the diagnostic identifier for this alarm handle.</summary>
public string DiagnosticId => "fake-alarm";
}
}
@@ -8,6 +8,9 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
[Trait("Category", "Unit")]
public sealed class OpcUaClientAttributeMappingTests
{
/// <summary>Verifies that MapUpstreamDataType recognizes standard builtin OPC UA types.</summary>
/// <param name="typeId">The OPC UA data type ID.</param>
/// <param name="expected">The expected driver data type.</param>
[Theory]
[InlineData((uint)DataTypes.Boolean, DriverDataType.Boolean)]
[InlineData((uint)DataTypes.SByte, DriverDataType.Int16)]
@@ -28,6 +31,7 @@ public sealed class OpcUaClientAttributeMappingTests
OpcUaClientDriver.MapUpstreamDataType(nodeId).ShouldBe(expected);
}
/// <summary>Verifies that MapUpstreamDataType maps SByte to Int16 since DriverDataType lacks 8-bit signed.</summary>
[Fact]
public void MapUpstreamDataType_maps_SByte_to_Int16_since_DriverDataType_lacks_8bit_signed()
{
@@ -35,6 +39,7 @@ public sealed class OpcUaClientAttributeMappingTests
OpcUaClientDriver.MapUpstreamDataType(new NodeId((uint)DataTypes.SByte)).ShouldBe(DriverDataType.Int16);
}
/// <summary>Verifies that MapUpstreamDataType maps Byte to UInt16 not Int16.</summary>
[Fact]
public void MapUpstreamDataType_maps_Byte_to_UInt16_not_Int16()
{
@@ -44,6 +49,7 @@ public sealed class OpcUaClientAttributeMappingTests
OpcUaClientDriver.MapUpstreamDataType(new NodeId((uint)DataTypes.Byte)).ShouldBe(DriverDataType.UInt16);
}
/// <summary>Verifies that MapUpstreamDataType falls back to String for unknown custom types.</summary>
[Fact]
public void MapUpstreamDataType_falls_back_to_String_for_unknown_custom_types()
{
@@ -51,12 +57,16 @@ public sealed class OpcUaClientAttributeMappingTests
OpcUaClientDriver.MapUpstreamDataType(new NodeId("CustomStruct", 2)).ShouldBe(DriverDataType.String);
}
/// <summary>Verifies that MapUpstreamDataType handles UtcTime as DateTime.</summary>
[Fact]
public void MapUpstreamDataType_handles_UtcTime_as_DateTime()
{
OpcUaClientDriver.MapUpstreamDataType(new NodeId((uint)DataTypes.UtcTime)).ShouldBe(DriverDataType.DateTime);
}
/// <summary>Verifies that MapAccessLevelToSecurityClass respects the CurrentWrite bit.</summary>
/// <param name="accessLevel">The access level byte.</param>
/// <param name="expected">The expected security classification.</param>
[Theory]
[InlineData((byte)0, SecurityClassification.ViewOnly)] // no access flags set
[InlineData((byte)1, SecurityClassification.ViewOnly)] // CurrentRead only
@@ -8,6 +8,9 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
[Trait("Category", "Unit")]
public sealed class OpcUaClientCertAuthTests
{
/// <summary>
/// Verifies that BuildCertificateIdentity rejects missing certificate path.
/// </summary>
[Fact]
public void BuildCertificateIdentity_rejects_missing_path()
{
@@ -16,6 +19,9 @@ public sealed class OpcUaClientCertAuthTests
.Message.ShouldContain("UserCertificatePath");
}
/// <summary>
/// Verifies that BuildCertificateIdentity rejects nonexistent certificate file.
/// </summary>
[Fact]
public void BuildCertificateIdentity_rejects_nonexistent_file()
{
@@ -27,6 +33,9 @@ public sealed class OpcUaClientCertAuthTests
Should.Throw<FileNotFoundException>(() => OpcUaClientDriver.BuildCertificateIdentity(opts));
}
/// <summary>
/// Verifies that BuildCertificateIdentity loads a valid PFX with private key.
/// </summary>
[Fact]
public void BuildCertificateIdentity_loads_a_valid_PFX_with_private_key()
{
@@ -12,6 +12,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
[Trait("Category", "Unit")]
public sealed class OpcUaClientDiscoveryTests
{
/// <summary>Verifies that DiscoverAsync throws InvalidOperationException when not initialized.</summary>
[Fact]
public async Task DiscoverAsync_without_initialize_throws_InvalidOperationException()
{
@@ -21,6 +22,7 @@ public sealed class OpcUaClientDiscoveryTests
await drv.DiscoverAsync(builder, TestContext.Current.CancellationToken));
}
/// <summary>Verifies that DiscoverAsync rejects null builder argument.</summary>
[Fact]
public void DiscoverAsync_rejects_null_builder()
{
@@ -29,6 +31,7 @@ public sealed class OpcUaClientDiscoveryTests
await drv.DiscoverAsync(null!, TestContext.Current.CancellationToken));
}
/// <summary>Verifies that discovery configuration has sensible defaults.</summary>
[Fact]
public void Discovery_caps_are_sensible_defaults()
{
@@ -38,17 +41,41 @@ public sealed class OpcUaClientDiscoveryTests
opts.BrowseRoot.ShouldBeNull("null = default to ObjectsFolder i=85");
}
/// <summary>Test builder that provides no-op implementations for discovery tests.</summary>
private sealed class NullAddressSpaceBuilder : IAddressSpaceBuilder
{
/// <summary>Returns this builder (no-op).</summary>
/// <param name="browseName">The browse name of the folder.</param>
/// <param name="displayName">The display name of the folder.</param>
public IAddressSpaceBuilder Folder(string browseName, string displayName) => this;
/// <summary>Returns a stub handle.</summary>
/// <param name="browseName">The browse name of the variable.</param>
/// <param name="displayName">The display name of the variable.</param>
/// <param name="attributeInfo">The attribute information for the variable.</param>
public IVariableHandle Variable(string browseName, string displayName, DriverAttributeInfo attributeInfo)
=> new StubHandle();
/// <summary>No-op property addition.</summary>
/// <param name="browseName">The browse name of the property.</param>
/// <param name="dataType">The data type of the property.</param>
/// <param name="value">The property value.</param>
public void AddProperty(string browseName, DriverDataType dataType, object? value) { }
/// <summary>No-op alarm condition attachment.</summary>
/// <param name="sourceVariable">The source variable handle.</param>
/// <param name="alarmName">The alarm name.</param>
/// <param name="alarmInfo">The alarm attribute information.</param>
public void AttachAlarmCondition(IVariableHandle sourceVariable, string alarmName, DriverAttributeInfo alarmInfo) { }
/// <summary>Stub variable handle for testing.</summary>
private sealed class StubHandle : IVariableHandle
{
/// <summary>Gets the full reference as "stub".</summary>
public string FullReference => "stub";
/// <summary>Throws NotSupportedException.</summary>
/// <param name="info">The alarm condition information (unused).</param>
public IAlarmConditionSink MarkAsAlarmCondition(AlarmConditionInfo info) => throw new NotSupportedException();
}
}
@@ -12,6 +12,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
[Trait("Category", "Unit")]
public sealed class OpcUaClientDriverScaffoldTests
{
/// <summary>Verifies that default options target the standard OPC UA port with anonymous auth.</summary>
[Fact]
public void Default_options_target_standard_opcua_port_and_anonymous_auth()
{
@@ -23,6 +24,7 @@ public sealed class OpcUaClientDriverScaffoldTests
opts.AutoAcceptCertificates.ShouldBeFalse("production default must reject untrusted server certs");
}
/// <summary>Verifies that default timeouts match driver specification section 8.</summary>
[Fact]
public void Default_timeouts_match_driver_specs_section_8()
{
@@ -32,6 +34,7 @@ public sealed class OpcUaClientDriverScaffoldTests
opts.ReconnectPeriod.ShouldBe(TimeSpan.FromSeconds(5));
}
/// <summary>Verifies that driver reports type and ID before connecting.</summary>
[Fact]
public void Driver_reports_type_and_id_before_connect()
{
@@ -41,6 +44,7 @@ public sealed class OpcUaClientDriverScaffoldTests
drv.GetHealth().State.ShouldBe(DriverState.Unknown);
}
/// <summary>Verifies that Initialize against unreachable endpoint transitions to Faulted and throws.</summary>
[Fact]
public async Task Initialize_against_unreachable_endpoint_transitions_to_Faulted_and_throws()
{
@@ -67,6 +71,7 @@ public sealed class OpcUaClientDriverScaffoldTests
health.LastError.ShouldNotBeNull();
}
/// <summary>Verifies that Reinitialize against unreachable endpoint re-throws the error.</summary>
[Fact]
public async Task Reinitialize_against_unreachable_endpoint_re_throws()
{
@@ -7,6 +7,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
[Trait("Category", "Unit")]
public sealed class OpcUaClientFailoverTests
{
/// <summary>Verifies that ResolveEndpointCandidates prefers EndpointUrls when provided.</summary>
[Fact]
public void ResolveEndpointCandidates_prefers_EndpointUrls_when_provided()
{
@@ -21,6 +22,7 @@ public sealed class OpcUaClientFailoverTests
list[1].ShouldBe("opc.tcp://backup:4841");
}
/// <summary>Verifies that ResolveEndpointCandidates falls back to single EndpointUrl when list is empty.</summary>
[Fact]
public void ResolveEndpointCandidates_falls_back_to_single_EndpointUrl_when_list_empty()
{
@@ -30,6 +32,7 @@ public sealed class OpcUaClientFailoverTests
list[0].ShouldBe("opc.tcp://only:4840");
}
/// <summary>Verifies that an empty EndpointUrls list is treated as a fallback to EndpointUrl.</summary>
[Fact]
public void ResolveEndpointCandidates_empty_list_treated_as_fallback_to_EndpointUrl()
{
@@ -43,6 +46,7 @@ public sealed class OpcUaClientFailoverTests
OpcUaClientDriver.ResolveEndpointCandidates(opts).Count.ShouldBe(1);
}
/// <summary>Verifies that HostName uses the first candidate before connection.</summary>
[Fact]
public void HostName_uses_first_candidate_before_connect()
{
@@ -55,6 +59,7 @@ public sealed class OpcUaClientFailoverTests
"pre-connect the dashboard should show the first candidate URL so operators can link back");
}
/// <summary>Verifies that initializing against all unreachable endpoints throws AggregateException listing each.</summary>
[Fact]
public async Task Initialize_against_all_unreachable_endpoints_throws_AggregateException_listing_each()
{
@@ -8,6 +8,8 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
[Trait("Category", "Unit")]
public sealed class OpcUaClientHistoryTests
{
/// <summary>Verifies MapAggregateToNodeId returns standard Part 13 aggregate for every enum.</summary>
/// <param name="agg">The history aggregate type to test.</param>
[Theory]
[InlineData(HistoryAggregateType.Average)]
[InlineData(HistoryAggregateType.Minimum)]
@@ -22,6 +24,7 @@ public sealed class OpcUaClientHistoryTests
nodeId.NamespaceIndex.ShouldBe((ushort)0);
}
/// <summary>Verifies MapAggregateToNodeId rejects invalid enum values.</summary>
[Fact]
public void MapAggregateToNodeId_rejects_invalid_enum_value()
{
@@ -30,6 +33,7 @@ public sealed class OpcUaClientHistoryTests
OpcUaClientDriver.MapAggregateToNodeId((HistoryAggregateType)99));
}
/// <summary>Verifies ReadRawAsync throws without initialization.</summary>
[Fact]
public async Task ReadRawAsync_without_initialize_throws_InvalidOperationException()
{
@@ -40,6 +44,7 @@ public sealed class OpcUaClientHistoryTests
TestContext.Current.CancellationToken));
}
/// <summary>Verifies ReadRawAsync with malformed NodeId returns empty result.</summary>
[Fact]
public async Task ReadRawAsync_with_malformed_NodeId_returns_empty_result_not_throw()
{
@@ -52,6 +57,7 @@ public sealed class OpcUaClientHistoryTests
await Task.CompletedTask;
}
/// <summary>Verifies ReadProcessedAsync throws without initialization.</summary>
[Fact]
public async Task ReadProcessedAsync_without_initialize_throws_InvalidOperationException()
{
@@ -63,6 +69,7 @@ public sealed class OpcUaClientHistoryTests
TestContext.Current.CancellationToken));
}
/// <summary>Verifies ReadAtTimeAsync throws without initialization.</summary>
[Fact]
public async Task ReadAtTimeAsync_without_initialize_throws_InvalidOperationException()
{
@@ -73,6 +80,7 @@ public sealed class OpcUaClientHistoryTests
TestContext.Current.CancellationToken));
}
/// <summary>Verifies ReadEventsAsync throws NotSupportedException as documented.</summary>
[Fact]
public async Task ReadEventsAsync_throws_NotSupportedException_as_documented()
{
@@ -26,6 +26,7 @@ public sealed class OpcUaClientLowFindingsRegressionTests
// The decision branches are pure logic; assert them against the SDK constants so a
// regression rewriting `valueRank >= 0` shows up in CI.
/// <summary>Verifies that ValueRank constants match the OPC UA Part 3 specification values.</summary>
[Fact]
public void ValueRank_constants_have_the_OPCUA_Part3_spec_values()
{
@@ -39,6 +40,9 @@ public sealed class OpcUaClientLowFindingsRegressionTests
ValueRanks.OneDimension.ShouldBe(1);
}
/// <summary>Verifies that IsArray decision matches the valueRank >= 0 boundary.</summary>
/// <param name="valueRank">The OPC UA value rank to test.</param>
/// <param name="expectedIsArray">Whether the value rank should be treated as an array.</param>
[Theory]
[InlineData(-3, false)] // ScalarOrOneDimension — conservatively treated as scalar
[InlineData(-2, false)] // Any — conservatively treated as scalar
@@ -69,6 +73,7 @@ public sealed class OpcUaClientLowFindingsRegressionTests
// with the *same delegate instance*. Confirm that further notifications do not
// invoke the handler.
/// <summary>Verifies that RemoteSubscription record carries handler delegates for detachment.</summary>
[Fact]
public void RemoteSubscription_record_carries_handler_delegates_so_they_can_be_detached()
{
@@ -95,6 +100,7 @@ public sealed class OpcUaClientLowFindingsRegressionTests
"so UnsubscribeAsync/ShutdownAsync can detach the Notification delegate before disposing the session.");
}
/// <summary>Verifies that RemoteAlarmSubscription record carries handler delegate for detachment.</summary>
[Fact]
public void RemoteAlarmSubscription_record_carries_handler_delegate_so_it_can_be_detached()
{
@@ -114,6 +120,7 @@ public sealed class OpcUaClientLowFindingsRegressionTests
"disposing the session.");
}
/// <summary>Verifies that UnsubscribeAsync with unknown handle does not throw after the fix.</summary>
[Fact]
public async Task UnsubscribeAsync_unknown_handle_does_not_throw_after_fix()
{
@@ -124,6 +131,7 @@ public sealed class OpcUaClientLowFindingsRegressionTests
await drv.UnsubscribeAsync(new FakeHandle(), TestContext.Current.CancellationToken));
}
/// <summary>Verifies that UnsubscribeAlarmsAsync with unknown handle does not throw after the fix.</summary>
[Fact]
public async Task UnsubscribeAlarmsAsync_unknown_handle_does_not_throw_after_fix()
{
@@ -132,13 +140,17 @@ public sealed class OpcUaClientLowFindingsRegressionTests
await drv.UnsubscribeAlarmsAsync(new FakeAlarmHandle(), TestContext.Current.CancellationToken));
}
/// <summary>Fake subscription handle for testing.</summary>
private sealed class FakeHandle : Core.Abstractions.ISubscriptionHandle
{
/// <summary>Gets the diagnostic identifier for this handle.</summary>
public string DiagnosticId => "fake-sub";
}
/// <summary>Fake alarm subscription handle for testing.</summary>
private sealed class FakeAlarmHandle : Core.Abstractions.IAlarmSubscriptionHandle
{
/// <summary>Gets the diagnostic identifier for this handle.</summary>
public string DiagnosticId => "fake-alarm-sub";
}
}
@@ -22,6 +22,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests
{
// ---- Driver.OpcUaClient-009 ----
/// <summary>Verifies that WriteAsync without session returns BadCommunicationError, not BadTimeout.</summary>
[Fact]
public async Task WriteAsync_without_session_returns_BadCommunicationError_not_BadTimeout()
{
@@ -38,6 +39,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests
// ---- Driver.OpcUaClient-010 ----
/// <summary>Verifies that Byte data type maps to UInt16, not Int16.</summary>
[Fact]
public void MapUpstreamDataType_Byte_maps_to_UInt16_unsigned_family()
{
@@ -47,6 +49,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests
result.ShouldNotBe(DriverDataType.Int16, "Byte is unsigned; Int16 is signed — wrong family");
}
/// <summary>Verifies that SByte data type maps to Int16 signed family.</summary>
[Fact]
public void MapUpstreamDataType_SByte_still_maps_to_Int16_signed_family()
{
@@ -57,6 +60,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests
// ---- Driver.OpcUaClient-012 ----
/// <summary>Verifies that AutoAcceptCertificates defaults to false.</summary>
[Fact]
public void AutoAcceptCertificates_default_is_false_secure_by_default()
{
@@ -64,6 +68,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests
"production default must reject untrusted server certs to prevent MITM");
}
/// <summary>Verifies that InitializeAsync with AutoAccept emits a warning log.</summary>
[Fact]
public async Task InitializeAsync_AutoAccept_emits_warning_log()
{
@@ -93,6 +98,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests
// ---- Driver.OpcUaClient-013 ----
/// <summary>Verifies that GetMemoryFootprint returns zero before discovery.</summary>
[Fact]
public void GetMemoryFootprint_returns_zero_before_first_discovery()
{
@@ -101,6 +107,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests
drv.GetMemoryFootprint().ShouldBe(0L);
}
/// <summary>Verifies that FlushOptionalCachesAsync completes without throwing.</summary>
[Fact]
public async Task FlushOptionalCachesAsync_completes_without_throwing()
{
@@ -110,6 +117,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests
await drv.FlushOptionalCachesAsync(TestContext.Current.CancellationToken));
}
/// <summary>Verifies that FlushOptionalCachesAsync resets the footprint counter.</summary>
[Fact]
public async Task FlushOptionalCachesAsync_resets_footprint_counter()
{
@@ -125,6 +133,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests
// ---- Driver.OpcUaClient-015: pure-logic paths ----
/// <summary>Verifies that MapSeverity thresholds match OPC UA AC Part 9 guidance.</summary>
[Fact]
public void MapSeverity_thresholds_match_opcua_ac_part9_guidance()
{
@@ -139,6 +148,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests
OpcUaClientDriver.MapSeverity(1000).ShouldBe(AlarmSeverity.Critical);
}
/// <summary>Verifies that driver health starts Unknown and is accessible before init.</summary>
[Fact]
public void Driver_health_starts_Unknown_and_is_accessible_before_init()
{
@@ -151,6 +161,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests
h.LastSuccessfulRead.ShouldBeNull();
}
/// <summary>Verifies that driver Session is null before init and RequireSession throws.</summary>
[Fact]
public void Driver_Session_is_null_before_init_so_RequireSession_throws()
{
@@ -160,6 +171,7 @@ public sealed class OpcUaClientMediumFindingsRegressionTests
drv.Session.ShouldBeNull();
}
/// <summary>Verifies that GetHostStatuses returns single entry keyed to configured endpoint.</summary>
[Fact]
public void GetHostStatuses_returns_single_entry_keyed_to_configured_endpoint()
{
@@ -177,11 +189,26 @@ public sealed class OpcUaClientMediumFindingsRegressionTests
private sealed class CapturingLogger : ILogger<OpcUaClientDriver>
{
/// <summary>Gets the captured log entries.</summary>
public List<(LogLevel Level, string Message)> Entries { get; } = [];
/// <summary>Begins a logical scope.</summary>
/// <typeparam name="TState">The type of the state object.</typeparam>
/// <param name="state">The state to scope.</param>
/// <returns>An IDisposable representing the scope.</returns>
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
/// <summary>Checks if logging is enabled for the specified level.</summary>
/// <param name="logLevel">The log level.</param>
/// <returns>True if logging is enabled.</returns>
public bool IsEnabled(LogLevel logLevel) => true;
/// <summary>Logs a message.</summary>
/// <typeparam name="TState">The type of the state object.</typeparam>
/// <param name="logLevel">The log level.</param>
/// <param name="eventId">The event ID.</param>
/// <param name="state">The state.</param>
/// <param name="exception">The exception.</param>
/// <param name="formatter">The formatter function.</param>
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state,
Exception? exception, Func<TState, Exception?, string> formatter)
{
@@ -14,6 +14,7 @@ public sealed class OpcUaClientNamespaceTests
{
// ---- Driver.OpcUaClient-004: TargetNamespaceKind startup enforcement ----
/// <summary>Verifies that Equipment namespace kind without mapping table is rejected.</summary>
[Fact]
public void ValidateNamespaceKind_Equipment_without_mapping_table_is_rejected()
{
@@ -24,6 +25,7 @@ public sealed class OpcUaClientNamespaceTests
.Message.ShouldContain("UnsMappingTable");
}
/// <summary>Verifies that Equipment namespace kind with mapping table passes.</summary>
[Fact]
public void ValidateNamespaceKind_Equipment_with_mapping_table_passes()
{
@@ -35,6 +37,7 @@ public sealed class OpcUaClientNamespaceTests
Should.NotThrow(() => OpcUaClientDriver.ValidateNamespaceKind(opts));
}
/// <summary>Verifies that SystemPlatform namespace kind with mapping table is rejected.</summary>
[Fact]
public void ValidateNamespaceKind_SystemPlatform_with_mapping_table_is_rejected()
{
@@ -48,6 +51,7 @@ public sealed class OpcUaClientNamespaceTests
.Message.ShouldContain("SystemPlatform");
}
/// <summary>Verifies that SystemPlatform namespace kind without mapping table passes.</summary>
[Fact]
public void ValidateNamespaceKind_SystemPlatform_without_mapping_table_passes()
{
@@ -55,6 +59,7 @@ public sealed class OpcUaClientNamespaceTests
Should.NotThrow(() => OpcUaClientDriver.ValidateNamespaceKind(opts));
}
/// <summary>Verifies that the default target namespace kind is Equipment.</summary>
[Fact]
public void Default_TargetNamespaceKind_is_Equipment()
{
@@ -64,10 +69,12 @@ public sealed class OpcUaClientNamespaceTests
// ---- Driver.OpcUaClient-004: server-stable NodeId encoding ----
/// <summary>Verifies that NamespaceMap.FromSession rejects null session.</summary>
[Fact]
public void NamespaceMap_FromSession_rejects_null_session() =>
Should.Throw<ArgumentNullException>(() => NamespaceMap.FromSession(null!));
/// <summary>Verifies that namespace 0 NodeId keeps compact form.</summary>
[Fact]
public void NamespaceMap_namespace0_NodeId_keeps_compact_form()
{
@@ -79,6 +86,7 @@ public sealed class OpcUaClientNamespaceTests
map.ToStableReference(coreNode).ShouldNotContain("nsu=");
}
/// <summary>Verifies that nonzero namespace NodeId is encoded with URI, not index.</summary>
[Fact]
public void NamespaceMap_nonzero_namespace_NodeId_is_encoded_with_uri_not_index()
{
@@ -92,6 +100,7 @@ public sealed class OpcUaClientNamespaceTests
stable.ShouldNotStartWith("ns=1");
}
/// <summary>Verifies that unknown namespace index falls back to raw form.</summary>
[Fact]
public void NamespaceMap_unknown_namespace_index_falls_back_to_raw_form()
{
@@ -102,6 +111,7 @@ public sealed class OpcUaClientNamespaceTests
Should.NotThrow(() => map.ToStableReference(node));
}
/// <summary>Verifies that namespace index and URI lookups are bidirectional.</summary>
[Fact]
public void NamespaceMap_index_and_uri_lookups_are_bidirectional()
{
@@ -113,6 +123,7 @@ public sealed class OpcUaClientNamespaceTests
map.UriForIndex(99).ShouldBeNull();
}
/// <summary>Verifies that NamespaceMap.TryResolve rejects empty and null input.</summary>
[Fact]
public void NamespaceMap_TryResolve_rejects_empty_and_null_input()
{
@@ -12,6 +12,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
[Trait("Category", "Unit")]
public sealed class OpcUaClientReadWriteTests
{
/// <summary>Verifies that ReadAsync throws InvalidOperationException when not initialized.</summary>
[Fact]
public async Task ReadAsync_without_initialize_throws_InvalidOperationException()
{
@@ -20,6 +21,7 @@ public sealed class OpcUaClientReadWriteTests
await drv.ReadAsync(["ns=2;s=Demo"], TestContext.Current.CancellationToken));
}
/// <summary>Verifies that WriteAsync throws InvalidOperationException when not initialized.</summary>
[Fact]
public async Task WriteAsync_without_initialize_throws_InvalidOperationException()
{
@@ -11,12 +11,14 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
[Trait("Category", "Unit")]
public sealed class OpcUaClientReconnectTests
{
/// <summary>Verifies that the default reconnect period matches the driver specification of 5 seconds.</summary>
[Fact]
public void Default_ReconnectPeriod_matches_driver_specs_5_seconds()
{
new OpcUaClientDriverOptions().ReconnectPeriod.ShouldBe(TimeSpan.FromSeconds(5));
}
/// <summary>Verifies that reconnect period can be configured for aggressive or relaxed retries.</summary>
[Fact]
public void Options_ReconnectPeriod_is_configurable_for_aggressive_or_relaxed_retry()
{
@@ -24,6 +26,7 @@ public sealed class OpcUaClientReconnectTests
opts.ReconnectPeriod.ShouldBe(TimeSpan.FromMilliseconds(500));
}
/// <summary>Verifies that the driver starts with no reconnect handler active before initialization.</summary>
[Fact]
public void Driver_starts_with_no_reconnect_handler_active_pre_init()
{
@@ -7,6 +7,8 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
[Trait("Category", "Unit")]
public sealed class OpcUaClientSecurityPolicyTests
{
/// <summary>Verifies that every security policy enum value maps to a known non-empty URI.</summary>
/// <param name="policy">The OPC UA security policy to map.</param>
[Theory]
[InlineData(OpcUaSecurityPolicy.None)]
[InlineData(OpcUaSecurityPolicy.Basic128Rsa15)]
@@ -24,6 +26,7 @@ public sealed class OpcUaClientSecurityPolicyTests
uri.ShouldContain(policy.ToString());
}
/// <summary>Verifies that None security policy maps to the SDK None URI.</summary>
[Fact]
public void MapSecurityPolicy_None_matches_SDK_None_URI()
{
@@ -31,6 +34,7 @@ public sealed class OpcUaClientSecurityPolicyTests
.ShouldBe(SecurityPolicies.None);
}
/// <summary>Verifies that Basic256Sha256 policy maps to the SDK URI.</summary>
[Fact]
public void MapSecurityPolicy_Basic256Sha256_matches_SDK_URI()
{
@@ -38,6 +42,7 @@ public sealed class OpcUaClientSecurityPolicyTests
.ShouldBe(SecurityPolicies.Basic256Sha256);
}
/// <summary>Verifies that Aes256_Sha256_RsaPss policy maps to the SDK URI.</summary>
[Fact]
public void MapSecurityPolicy_Aes256_Sha256_RsaPss_matches_SDK_URI()
{
@@ -45,6 +50,7 @@ public sealed class OpcUaClientSecurityPolicyTests
.ShouldBe(SecurityPolicies.Aes256_Sha256_RsaPss);
}
/// <summary>Verifies that every security policy enum value has a mapping.</summary>
[Fact]
public void Every_enum_value_has_a_mapping()
{
@@ -13,6 +13,7 @@ namespace ZB.MOM.WW.OtOpcUa.Driver.OpcUaClient.Tests;
[Trait("Category", "Unit")]
public sealed class OpcUaClientSubscribeAndProbeTests
{
/// <summary>Verifies that subscribe without initialization throws InvalidOperationException.</summary>
[Fact]
public async Task SubscribeAsync_without_initialize_throws_InvalidOperationException()
{
@@ -21,6 +22,7 @@ public sealed class OpcUaClientSubscribeAndProbeTests
await drv.SubscribeAsync(["ns=2;s=Demo"], TimeSpan.FromMilliseconds(100), TestContext.Current.CancellationToken));
}
/// <summary>Verifies that unsubscribe with unknown handle is a no-op.</summary>
[Fact]
public async Task UnsubscribeAsync_with_unknown_handle_is_noop()
{
@@ -30,6 +32,7 @@ public sealed class OpcUaClientSubscribeAndProbeTests
await drv.UnsubscribeAsync(new FakeHandle(), TestContext.Current.CancellationToken);
}
/// <summary>Verifies that GetHostStatuses returns endpoint URL row before initialization.</summary>
[Fact]
public void GetHostStatuses_returns_endpoint_url_row_pre_init()
{
@@ -43,8 +46,10 @@ public sealed class OpcUaClientSubscribeAndProbeTests
rows[0].State.ShouldBe(HostState.Unknown);
}
/// <summary>A fake subscription handle for testing.</summary>
private sealed class FakeHandle : ISubscriptionHandle
{
/// <inheritdoc />
public string DiagnosticId => "fake";
}
}