diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/CliArguments.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/CliArguments.cs index 5854613..054e1fd 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/CliArguments.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/CliArguments.cs @@ -44,6 +44,7 @@ internal sealed class CliArguments /// Returns whether the named flag was present in the arguments. /// The flag name (without '--' prefix). + /// True if the flag was present; otherwise false. public bool HasFlag(string name) { return _flags.Contains(name); @@ -51,6 +52,7 @@ internal sealed class CliArguments /// Returns the value for a named argument, or null if absent. /// The argument name (without '--' prefix). + /// The argument value, or null if the argument was not provided. public string? GetOptional(string name) { return _values.TryGetValue(name, out string? value) @@ -60,6 +62,7 @@ internal sealed class CliArguments /// Returns the value for a required named argument, or throws if absent. /// The argument name (without '--' prefix). + /// The argument value. public string GetRequired(string name) { string? value = GetOptional(name); @@ -74,6 +77,7 @@ internal sealed class CliArguments /// Parses and returns an int32 argument, or the default value if absent. /// The argument name (without '--' prefix). /// The default value if the argument is absent; if null, the argument is required. + /// The parsed int32 value, or the default if absent. public int GetInt32(string name, int? defaultValue = null) { string? value = GetOptional(name); @@ -93,6 +97,7 @@ internal sealed class CliArguments /// Parses and returns a uint32 argument, or the default value if absent. /// The argument name (without '--' prefix). /// The default value if the argument is absent. + /// The parsed uint32 value, or the default if absent. public uint GetUInt32(string name, uint defaultValue) { string? value = GetOptional(name); @@ -104,6 +109,7 @@ internal sealed class CliArguments /// Parses and returns a uint64 argument, or the default value if absent. /// The argument name (without '--' prefix). /// The default value if the argument is absent. + /// The parsed uint64 value, or the default if absent. public ulong GetUInt64(string name, ulong defaultValue) { string? value = GetOptional(name); @@ -115,6 +121,7 @@ internal sealed class CliArguments /// Parses and returns a TimeSpan argument, or the default value if absent. Supports "ms", "s", and standard TimeSpan format. /// The argument name (without '--' prefix). /// The default value if the argument is absent. + /// The parsed TimeSpan value, or the default if absent. public TimeSpan GetDuration(string name, TimeSpan defaultValue) { string? value = GetOptional(name); diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/MxGatewayCliClientAdapter.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/MxGatewayCliClientAdapter.cs index 6601dcb..9a30c8c 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/MxGatewayCliClientAdapter.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/MxGatewayCliClientAdapter.cs @@ -100,7 +100,8 @@ internal sealed class MxGatewayCliClientAdapter : IMxGatewayCliClient return _galaxyClient.Value.WatchDeployEventsRawAsync(request, cancellationToken); } - /// + /// Disposes the galaxy client (if created) and the underlying gateway client. + /// A value task that completes when both clients are disposed. public async ValueTask DisposeAsync() { if (_galaxyClient.IsValueCreated) diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/MxGatewayCliSecretRedactor.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/MxGatewayCliSecretRedactor.cs index d4e3003..1a2a563 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/MxGatewayCliSecretRedactor.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/MxGatewayCliSecretRedactor.cs @@ -6,6 +6,7 @@ internal static class MxGatewayCliSecretRedactor /// Replaces occurrences of the API key in the value with a redacted placeholder. /// The message text to redact. /// The API key to remove; no redaction if null or empty. + /// The message text with any API key occurrence replaced by [redacted]. public static string Redact(string value, string? apiKey) { if (string.IsNullOrEmpty(value) || string.IsNullOrEmpty(apiKey)) diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/MxGatewayClientCli.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/MxGatewayClientCli.cs index 8f2ff53..184da00 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/MxGatewayClientCli.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Cli/MxGatewayClientCli.cs @@ -22,6 +22,7 @@ public static class MxGatewayClientCli /// Command-line arguments (command name followed by options). /// TextWriter for command output. /// TextWriter for error messages. + /// The process exit code (0 for success, 1 for error). public static int Run( string[] args, TextWriter standardOutput, @@ -38,6 +39,7 @@ public static class MxGatewayClientCli /// TextWriter for error messages. /// Optional factory to create the gateway client; defaults to MxGatewayClient.Create. /// Optional TextReader for batch-mode stdin; defaults to . + /// A task that resolves to the process exit code (0 for success, 1 for error). public static Task RunAsync( string[] args, TextWriter standardOutput, diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/BrowseChildrenSmokeTests.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/BrowseChildrenSmokeTests.cs index dd5f48e..943a106 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/BrowseChildrenSmokeTests.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/BrowseChildrenSmokeTests.cs @@ -14,6 +14,7 @@ public sealed class BrowseChildrenSmokeTests /// Verifies that BrowseChildren returns a non-zero cache sequence and /// a consistent children/child-has-children count from a live gateway. /// + /// A task that represents the asynchronous operation. [Fact(Skip = "Set MXGATEWAY_API_KEY and MXGATEWAY_ENDPOINT to enable.")] public async Task BrowseChildren_LiveGateway_ReturnsRootsWithCacheSequence() { diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/FakeGalaxyRepositoryTransport.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/FakeGalaxyRepositoryTransport.cs index 1ec00d1..e07d6de 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/FakeGalaxyRepositoryTransport.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/FakeGalaxyRepositoryTransport.cs @@ -8,14 +8,10 @@ namespace ZB.MOM.WW.MxGateway.Client.Tests; /// internal sealed class FakeGalaxyRepositoryTransport(MxGatewayClientOptions options) : IGalaxyRepositoryClientTransport { - /// - /// Gets the gateway client options. - /// + /// public MxGatewayClientOptions Options { get; } = options; - /// - /// Gets the raw gRPC client; always null for the fake. - /// + /// public GalaxyRepository.GalaxyRepositoryClient? RawClient => null; /// @@ -66,11 +62,7 @@ internal sealed class FakeGalaxyRepositoryTransport(MxGatewayClientOptions optio /// public Queue DiscoverHierarchyExceptions { get; } = new(); - /// - /// Records the request and either throws a queued exception or returns the configured reply. - /// - /// The TestConnectionRequest to process. - /// Call options specifying RPC behavior. + /// public Task TestConnectionAsync( TestConnectionRequest request, CallOptions callOptions) @@ -84,11 +76,7 @@ internal sealed class FakeGalaxyRepositoryTransport(MxGatewayClientOptions optio return Task.FromResult(TestConnectionReply); } - /// - /// Records the request and either throws a queued exception or returns the configured reply. - /// - /// The GetLastDeployTimeRequest to process. - /// Call options specifying RPC behavior. + /// public Task GetLastDeployTimeAsync( GetLastDeployTimeRequest request, CallOptions callOptions) @@ -102,11 +90,7 @@ internal sealed class FakeGalaxyRepositoryTransport(MxGatewayClientOptions optio return Task.FromResult(GetLastDeployTimeReply); } - /// - /// Records the request and either throws a queued exception or returns the configured reply. - /// - /// The DiscoverHierarchyRequest to process. - /// Call options specifying RPC behavior. + /// public Task DiscoverHierarchyAsync( DiscoverHierarchyRequest request, CallOptions callOptions) @@ -135,11 +119,7 @@ internal sealed class FakeGalaxyRepositoryTransport(MxGatewayClientOptions optio /// Queue of exceptions to throw from BrowseChildren; dequeued in FIFO order. public Queue BrowseChildrenExceptions { get; } = new(); - /// - /// Records the request and either throws a queued exception or returns the configured reply. - /// - /// The BrowseChildrenRequest to process. - /// Call options specifying RPC behavior. + /// public Task BrowseChildrenAsync( BrowseChildrenRequest request, CallOptions callOptions) @@ -177,11 +157,7 @@ internal sealed class FakeGalaxyRepositoryTransport(MxGatewayClientOptions optio /// public Func? WatchDeployEventsBeforeYield { get; set; } - /// - /// Records the request and streams events, checking for queued exceptions and calling WatchDeployEventsBeforeYield before each event. - /// - /// The WatchDeployEventsRequest to process. - /// Call options specifying RPC behavior. + /// public async IAsyncEnumerable WatchDeployEventsAsync( WatchDeployEventsRequest request, CallOptions callOptions) diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/FakeGatewayTransport.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/FakeGatewayTransport.cs index 7e81274..cb677fc 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/FakeGatewayTransport.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/FakeGatewayTransport.cs @@ -11,14 +11,10 @@ internal sealed class FakeGatewayTransport(MxGatewayClientOptions options) : IMx private readonly Queue _invokeReplies = new(); private readonly List _events = []; - /// - /// Gets the gateway client options. - /// + /// public MxGatewayClientOptions Options { get; } = options; - /// - /// Gets null, since this is a test fake without a real gRPC client. - /// + /// public MxAccessGateway.MxAccessGatewayClient? RawClient => null; /// @@ -102,11 +98,7 @@ internal sealed class FakeGatewayTransport(MxGatewayClientOptions options) : IMx /// public Queue InvokeExceptions { get; } = new(); - /// - /// Verifies that the OpenSessionAsync call is recorded and returns the configured reply. - /// - /// The OpenSessionRequest to process. - /// Call options specifying RPC behavior. + /// public Task OpenSessionAsync( OpenSessionRequest request, CallOptions callOptions) @@ -120,11 +112,7 @@ internal sealed class FakeGatewayTransport(MxGatewayClientOptions options) : IMx return Task.FromResult(OpenSessionReply); } - /// - /// Verifies that the CloseSessionAsync call is recorded and returns the configured reply. - /// - /// The CloseSessionRequest to process. - /// Call options specifying RPC behavior. + /// public Task CloseSessionAsync( CloseSessionRequest request, CallOptions callOptions) @@ -138,11 +126,7 @@ internal sealed class FakeGatewayTransport(MxGatewayClientOptions options) : IMx return Task.FromResult(CloseSessionReply); } - /// - /// Verifies that the InvokeAsync call is recorded and returns the next enqueued reply. - /// - /// The MxCommandRequest to process. - /// Call options specifying RPC behavior. + /// public Task InvokeAsync( MxCommandRequest request, CallOptions callOptions) @@ -156,11 +140,7 @@ internal sealed class FakeGatewayTransport(MxGatewayClientOptions options) : IMx return Task.FromResult(_invokeReplies.Dequeue()); } - /// - /// Verifies that the StreamEventsAsync call is recorded and yields all enqueued events. - /// - /// The StreamEventsRequest to process. - /// Call options specifying RPC behavior. + /// public async IAsyncEnumerable StreamEventsAsync( StreamEventsRequest request, CallOptions callOptions) @@ -193,11 +173,7 @@ internal sealed class FakeGatewayTransport(MxGatewayClientOptions options) : IMx _events.Add(gatewayEvent); } - /// - /// Records the acknowledge call and returns the next enqueued reply (or default). - /// - /// The acknowledge alarm request. - /// Call options specifying RPC behavior. + /// public Task AcknowledgeAlarmAsync( AcknowledgeAlarmRequest request, CallOptions callOptions) @@ -218,11 +194,7 @@ internal sealed class FakeGatewayTransport(MxGatewayClientOptions options) : IMx }); } - /// - /// Records the query call and yields each enqueued snapshot. - /// - /// The query active alarms request. - /// Call options specifying RPC behavior. + /// public async IAsyncEnumerable QueryActiveAlarmsAsync( QueryActiveAlarmsRequest request, CallOptions callOptions) @@ -251,11 +223,7 @@ internal sealed class FakeGatewayTransport(MxGatewayClientOptions options) : IMx _activeAlarmSnapshots.Add(snapshot); } - /// - /// Records the stream-alarms call and yields each enqueued feed message. - /// - /// The stream alarms request. - /// Call options specifying RPC behavior. + /// public async IAsyncEnumerable StreamAlarmsAsync( StreamAlarmsRequest request, CallOptions callOptions) diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/GalaxyRepositoryClientTests.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/GalaxyRepositoryClientTests.cs index 351a9d2..184bde7 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/GalaxyRepositoryClientTests.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/GalaxyRepositoryClientTests.cs @@ -9,6 +9,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that TestConnectionAsync attaches the API key in request metadata and returns the Ok flag. /// + /// A task that represents the asynchronous operation. [Fact] public async Task TestConnectionAsync_AttachesApiKeyMetadataAndReturnsOkFlag() { @@ -27,6 +28,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that TestConnectionAsync returns false when the server reports NotOk. /// + /// A task that represents the asynchronous operation. [Fact] public async Task TestConnectionAsync_ReturnsFalseWhenServerReportsNotOk() { @@ -42,6 +44,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that GetLastDeployTimeAsync returns null when the server reports not present. /// + /// A task that represents the asynchronous operation. [Fact] public async Task GetLastDeployTimeAsync_ReturnsNullWhenNotPresent() { @@ -58,6 +61,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that GetLastDeployTimeAsync returns the timestamp when the server reports it present. /// + /// A task that represents the asynchronous operation. [Fact] public async Task GetLastDeployTimeAsync_ReturnsTimestampWhenPresent() { @@ -79,6 +83,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that DiscoverHierarchyAsync returns the objects from the server reply. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverHierarchyAsync_ReturnsObjectsFromReply() { @@ -141,6 +146,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that DiscoverHierarchyAsync propagates cancellation tokens to the transport. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverHierarchyAsync_PropagatesCancellationToTransport() { @@ -161,6 +167,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that TestConnectionAsync retries on transient gRPC failures. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverHierarchyAsync_WithRepeatedPageToken_ThrowsProtocolError() { @@ -184,6 +191,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that DiscoverHierarchyAsync maps typed filter options correctly to the request. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverHierarchyAsync_WithOptions_MapsTypedFilters() { @@ -218,6 +226,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that TestConnectionAsync retries on transient gRPC failures. /// + /// A task that represents the asynchronous operation. [Fact] public async Task TestConnectionAsync_RetriesOnTransientGrpcFailure() { @@ -235,6 +244,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that DiscoverHierarchyAsync retries on transient gRPC failures. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverHierarchyAsync_RetriesOnTransientGrpcFailure() { @@ -251,6 +261,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that WatchDeployEventsAsync delivers the bootstrap event. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WatchDeployEventsAsync_DeliversBootstrapEvent() { @@ -287,6 +298,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that WatchDeployEventsAsync delivers multiple events in order. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WatchDeployEventsAsync_DeliversMultipleEventsInOrder() { @@ -325,6 +337,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that WatchDeployEventsAsync stops iteration cleanly when cancelled. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WatchDeployEventsAsync_CancellationStopsIterationCleanly() { @@ -369,6 +382,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that WatchDeployEventsAsync throws ObjectDisposedException after the client is disposed. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WatchDeployEventsAsync_ThrowsAfterDisposal() { @@ -384,6 +398,7 @@ public sealed class GalaxyRepositoryClientTests /// /// Verifies that TestConnectionAsync throws ObjectDisposedException after the client is disposed. /// + /// A task that represents the asynchronous operation. [Fact] public async Task TestConnectionAsync_ThrowsAfterDisposal() { diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/LazyBrowseNodeTests.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/LazyBrowseNodeTests.cs index b4d5452..79e1781 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/LazyBrowseNodeTests.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/LazyBrowseNodeTests.cs @@ -12,6 +12,7 @@ public sealed class LazyBrowseNodeTests /// Verifies that calling BrowseAsync with no parent returns the root nodes /// from the first BrowseChildren reply and surfaces the per-child has-children hint. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Browse_NoParent_ReturnsRoots() { @@ -36,6 +37,7 @@ public sealed class LazyBrowseNodeTests /// /// Verifies that ExpandAsync populates Children and marks the node expanded after one RPC. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Expand_PopulatesChildrenAndMarksExpanded() { @@ -62,6 +64,7 @@ public sealed class LazyBrowseNodeTests /// /// Verifies that a second ExpandAsync call is a no-op and issues no additional RPC. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Expand_CalledTwice_NoSecondRpc() { @@ -86,6 +89,7 @@ public sealed class LazyBrowseNodeTests /// /// Verifies that an RPC failure (NotFound) during expand is wrapped in MxGatewayException. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Expand_UnknownParent_ThrowsMxGatewayException() { @@ -113,6 +117,7 @@ public sealed class LazyBrowseNodeTests /// /// Verifies that ExpandAsync drains multi-page sibling replies and forwards the page token. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Expand_MultiPageSiblings_GathersAllPages() { @@ -147,6 +152,7 @@ public sealed class LazyBrowseNodeTests /// /// Verifies that ten concurrent ExpandAsync calls issue exactly one RPC, not ten. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Expand_CalledConcurrently_OnlyFiresOneRpc() { @@ -178,6 +184,7 @@ public sealed class LazyBrowseNodeTests /// /// Verifies that BrowseChildrenOptions filter fields are forwarded to the BrowseChildren request. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Browse_WithFilter_ForwardsToRequest() { diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayClientAlarmsTests.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayClientAlarmsTests.cs index b178944..61582b6 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayClientAlarmsTests.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayClientAlarmsTests.cs @@ -12,6 +12,7 @@ namespace ZB.MOM.WW.MxGateway.Client.Tests; public sealed class MxGatewayClientAlarmsTests { /// AcknowledgeAlarmAsync records request and returns reply. + /// A task that represents the asynchronous operation. [Fact] public async Task AcknowledgeAlarmAsync_RecordsRequestShapeAndReturnsReply() { @@ -48,6 +49,7 @@ public sealed class MxGatewayClientAlarmsTests } /// AcknowledgeAlarmAsync honors cancellation. + /// A task that represents the asynchronous operation. [Fact] public async Task AcknowledgeAlarmAsync_HonorsCancellation() { @@ -72,6 +74,7 @@ public sealed class MxGatewayClientAlarmsTests } /// AcknowledgeAlarmAsync maps unauthenticated RPC exception to typed exception. + /// A task that represents the asynchronous operation. [Fact] public async Task AcknowledgeAlarmAsync_MapsUnauthenticated_RpcException_ToTypedException() { @@ -97,6 +100,7 @@ public sealed class MxGatewayClientAlarmsTests } /// QueryActiveAlarmsAsync streams enqueued snapshots. + /// A task that represents the asynchronous operation. [Fact] public async Task QueryActiveAlarmsAsync_StreamsEnqueuedSnapshots() { @@ -122,6 +126,7 @@ public sealed class MxGatewayClientAlarmsTests } /// QueryActiveAlarmsAsync passes filter prefix. + /// A task that represents the asynchronous operation. [Fact] public async Task QueryActiveAlarmsAsync_PassesFilterPrefix() { @@ -142,6 +147,7 @@ public sealed class MxGatewayClientAlarmsTests } /// QueryActiveAlarmsAsync honors cancellation during enumeration. + /// A task that represents the asynchronous operation. [Fact] public async Task QueryActiveAlarmsAsync_HonorsCancellationDuringEnumeration() { diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayClientCliTests.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayClientCliTests.cs index a63ec30..6018771 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayClientCliTests.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayClientCliTests.cs @@ -24,6 +24,7 @@ public sealed class MxGatewayClientCliTests } /// Verifies that the version command with --json flag prints JSON protocol versions. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_VersionJson_PrintsJsonProtocolVersions() { @@ -38,6 +39,7 @@ public sealed class MxGatewayClientCliTests } /// Verifies that the write command builds a write request and prints JSON reply. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_Write_BuildsWriteCommandAndPrintsJsonReply() { @@ -83,6 +85,7 @@ public sealed class MxGatewayClientCliTests } /// Verifies that error output redacts sensitive API key values. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_ErrorOutput_RedactsApiKey() { @@ -107,6 +110,7 @@ public sealed class MxGatewayClientCliTests } /// Verifies that stream-events with max-events limit stops output in non-JSON format. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_StreamEvents_WithMaxEventsStopsNonJsonOutput() { @@ -149,6 +153,7 @@ public sealed class MxGatewayClientCliTests /// Verifies that stream-alarms with --max-events stops output and distinguishes payload cases. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_StreamAlarms_WithMaxEventsStopsAndDistinguishesPayloadCases() { @@ -188,6 +193,7 @@ public sealed class MxGatewayClientCliTests } /// Verifies that acknowledge-alarm builds a request and prints the JSON reply. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_AcknowledgeAlarm_BuildsRequestAndPrintsJsonReply() { @@ -230,6 +236,7 @@ public sealed class MxGatewayClientCliTests } /// Verifies that smoke command closes opened session when a command fails. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_Smoke_WhenCommandFails_ClosesOpenedSession() { @@ -261,6 +268,7 @@ public sealed class MxGatewayClientCliTests } /// Verifies that galaxy-test-connection command prints JSON reply. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_GalaxyTestConnection_PrintsJsonReply() { @@ -291,6 +299,7 @@ public sealed class MxGatewayClientCliTests } /// Verifies that galaxy-discover command prints hierarchy summary. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_GalaxyDiscover_PrintsHierarchySummary() { @@ -361,6 +370,7 @@ public sealed class MxGatewayClientCliTests } /// Verifies that galaxy-watch command prints text output for deploy events. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_GalaxyWatch_PrintsTextOutputForEvents() { @@ -415,6 +425,7 @@ public sealed class MxGatewayClientCliTests } /// Verifies that galaxy-watch with --json emits one JSON object per event. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_GalaxyWatch_JsonEmitsOneObjectPerEvent() { @@ -450,6 +461,7 @@ public sealed class MxGatewayClientCliTests } /// Verifies that batch mode dispatches a single version command and emits the EOR sentinel. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_Batch_DispatchesVersionAndWritesEndOfRecord() { @@ -476,6 +488,7 @@ public sealed class MxGatewayClientCliTests } /// Verifies that batch mode routes per-command errors to stdout as JSON between EOR markers. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_Batch_WritesErrorsToStdoutAsJson() { @@ -520,6 +533,7 @@ public sealed class MxGatewayClientCliTests /// against exit code 0. /// /// The alarm subcommand to validate (e.g. "stream-alarms", "acknowledge-alarm"). + /// A task that represents the asynchronous operation. [Theory] [InlineData("stream-alarms")] [InlineData("acknowledge-alarm")] @@ -574,6 +588,7 @@ public sealed class MxGatewayClientCliTests /// against a zero server handle. The fix must fail loudly with a /// descriptive . /// + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_BenchReadBulk_WhenRegisterReplyMissingTypedPayload_FailsLoudly() { @@ -624,6 +639,7 @@ public sealed class MxGatewayClientCliTests /// kept spinning until --duration-seconds elapsed. After the fix /// the bench must exit promptly when the supplied token cancels. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_BenchReadBulk_WhenSteadyStateLoopReceivesCancellation_ExitsPromptly() { @@ -718,6 +734,7 @@ public sealed class MxGatewayClientCliTests /// to ~49.7 days. The fix must reject negatives with a clear error. /// /// The bulk-read subcommand to validate (e.g. "read-bulk", "bench-read-bulk"). + /// A task that represents the asynchronous operation. [Theory] [InlineData("read-bulk")] [InlineData("bench-read-bulk")] @@ -880,7 +897,8 @@ public sealed class MxGatewayClientCliTests /// Optional per-call handler that overrides queue-based behaviour. public Func>? InvokeHandler { get; init; } - /// + /// Releases resources held by the fake CLI client. + /// A completed value task. public ValueTask DisposeAsync() { return ValueTask.CompletedTask; diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayClientSessionTests.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayClientSessionTests.cs index 1052a8d..66bc654 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayClientSessionTests.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayClientSessionTests.cs @@ -7,6 +7,7 @@ namespace ZB.MOM.WW.MxGateway.Client.Tests; public sealed class MxGatewayClientSessionTests { /// Verifies that open session attaches API key metadata and cancellation token. + /// A task that represents the asynchronous operation. [Fact] public async Task OpenSessionRawAsync_AttachesApiKeyMetadataAndCancellation() { @@ -22,6 +23,7 @@ public sealed class MxGatewayClientSessionTests } /// Verifies that open session returns a session with the raw open reply. + /// A task that represents the asynchronous operation. [Fact] public async Task OpenSessionAsync_ReturnsSessionWithRawOpenReply() { @@ -37,6 +39,7 @@ public sealed class MxGatewayClientSessionTests } /// Verifies that register builds a register command and returns server handle. + /// A task that represents the asynchronous operation. [Fact] public async Task RegisterAsync_BuildsRegisterCommandAndReturnsServerHandle() { @@ -62,6 +65,7 @@ public sealed class MxGatewayClientSessionTests } /// Verifies that add item 2 builds a command with the specified context. + /// A task that represents the asynchronous operation. [Fact] public async Task AddItem2Async_BuildsAddItem2CommandWithContext() { @@ -87,6 +91,7 @@ public sealed class MxGatewayClientSessionTests } /// Verifies that write raw builds a write command with the raw value. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteRawAsync_BuildsWriteCommandWithRawValue() { @@ -118,6 +123,7 @@ public sealed class MxGatewayClientSessionTests } /// Verifies that write 2 raw builds a write 2 command with value and timestamp. + /// A task that represents the asynchronous operation. [Fact] public async Task Write2RawAsync_BuildsWrite2CommandWithValueAndTimestamp() { @@ -146,6 +152,7 @@ public sealed class MxGatewayClientSessionTests } /// Verifies that subscribe bulk builds one command and returns per-item results. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeBulkAsync_BuildsOneBulkCommandAndReturnsPerItemResults() { @@ -185,6 +192,7 @@ public sealed class MxGatewayClientSessionTests } /// Verifies that stream events yields events in the order received from the gateway. + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEventsAsync_YieldsEventsInGatewayOrder() { @@ -216,6 +224,7 @@ public sealed class MxGatewayClientSessionTests } /// Verifies that close is explicit and idempotent. + /// A task that represents the asynchronous operation. [Fact] public async Task CloseAsync_IsExplicitAndIdempotent() { @@ -232,6 +241,7 @@ public sealed class MxGatewayClientSessionTests } /// Verifies that invoke retries safe diagnostic commands on transient RPC failure. + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_RetriesSafeDiagnosticCommandOnTransientGrpcFailure() { @@ -256,6 +266,7 @@ public sealed class MxGatewayClientSessionTests } /// Verifies that open session does not retry on transient RPC failure. + /// A task that represents the asynchronous operation. [Fact] public async Task OpenSessionAsync_DoesNotRetryTransientGrpcFailure() { @@ -269,6 +280,7 @@ public sealed class MxGatewayClientSessionTests } /// Verifies that invoke does not retry write commands on transient RPC failure. + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_DoesNotRetryWriteCommand() { @@ -284,6 +296,7 @@ public sealed class MxGatewayClientSessionTests } /// Verifies that invoke helpers pass cancellation token to the transport. + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeHelpers_PassCancellationTokenToTransport() { diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayGeneratedContractTests.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayGeneratedContractTests.cs index 95fdd89..7c1593a 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayGeneratedContractTests.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client.Tests/MxGatewayGeneratedContractTests.cs @@ -3,6 +3,7 @@ namespace ZB.MOM.WW.MxGateway.Client.Tests; public sealed class MxGatewayGeneratedContractTests { /// Verifies that the generated gRPC client can be instantiated from the client factory. + /// A task that represents the asynchronous operation. [Fact] public async Task GeneratedGrpcClient_CanBeConstructedFromClientFactory() { diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/GalaxyRepositoryClient.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/GalaxyRepositoryClient.cs index 7d05638..0e7ceaa 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/GalaxyRepositoryClient.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/GalaxyRepositoryClient.cs @@ -337,6 +337,9 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable cancellationToken); } + /// Builds a from the provided options. + /// Browse children options to convert. + /// The constructed request message. internal static BrowseChildrenRequest BuildBrowseChildrenRequest(BrowseChildrenOptions options) { ArgumentNullException.ThrowIfNull(options); @@ -424,6 +427,7 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable /// /// Closes the gRPC channel and releases resources. /// + /// A task that represents the asynchronous dispose operation. public ValueTask DisposeAsync() { if (_disposed) @@ -493,6 +497,9 @@ public sealed class GalaxyRepositoryClient : IAsyncDisposable private static HttpMessageHandler CreateHttpHandler(MxGatewayClientOptions options) => CreateHttpHandlerForTests(options); + /// Creates an configured from the provided options for test use. + /// Client options used to configure TLS and timeouts. + /// The configured HTTP message handler. internal static SocketsHttpHandler CreateHttpHandlerForTests(MxGatewayClientOptions options) { SocketsHttpHandler handler = new() diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/GrpcGalaxyRepositoryClientTransport.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/GrpcGalaxyRepositoryClientTransport.cs index e51337e..1d8e4a2 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/GrpcGalaxyRepositoryClientTransport.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/GrpcGalaxyRepositoryClientTransport.cs @@ -10,9 +10,7 @@ internal sealed class GrpcGalaxyRepositoryClientTransport( MxGatewayClientOptions options, GalaxyRepository.GalaxyRepositoryClient rawClient) : IGalaxyRepositoryClientTransport { - /// - /// Gets the gateway client options. - /// + /// public MxGatewayClientOptions Options { get; } = options; /// @@ -91,7 +89,11 @@ internal sealed class GrpcGalaxyRepositoryClientTransport( } } - /// + /// Streams deploy events from the Galaxy Repository, using an explicit cancellation token that overrides the call options token when provided. + /// The watch deploy events request. + /// Call options for the underlying gRPC call. + /// Optional cancellation token; takes precedence over the token in when cancellable. + /// An async enumerable of deploy events. public async IAsyncEnumerable WatchDeployEventsAsync( WatchDeployEventsRequest request, CallOptions callOptions, diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/GrpcMxGatewayClientTransport.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/GrpcMxGatewayClientTransport.cs index 5ff4e75..eac5b26 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/GrpcMxGatewayClientTransport.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/GrpcMxGatewayClientTransport.cs @@ -10,9 +10,7 @@ internal sealed class GrpcMxGatewayClientTransport( MxGatewayClientOptions options, MxAccessGateway.MxAccessGatewayClient rawClient) : IMxGatewayClientTransport { - /// - /// Gets the gateway client options. - /// + /// public MxGatewayClientOptions Options { get; } = options; /// @@ -74,7 +72,11 @@ internal sealed class GrpcMxGatewayClientTransport( } } - /// + /// Streams MXAccess events from the gateway, forwarding an explicit cancellation token to the stream reader. + /// The stream events request. + /// gRPC call options. + /// Token to cancel the streaming enumeration. + /// An async enumerable of MXAccess events. public async IAsyncEnumerable StreamEventsAsync( StreamEventsRequest request, CallOptions callOptions, @@ -133,7 +135,11 @@ internal sealed class GrpcMxGatewayClientTransport( } } - /// + /// Queries active alarms from the gateway, forwarding an explicit cancellation token to the stream reader. + /// The query active alarms request. + /// gRPC call options. + /// Token to cancel the streaming enumeration. + /// An async enumerable of active alarm snapshots. public async IAsyncEnumerable QueryActiveAlarmsAsync( QueryActiveAlarmsRequest request, CallOptions callOptions, @@ -175,7 +181,11 @@ internal sealed class GrpcMxGatewayClientTransport( return QueryActiveAlarmsAsync(request, callOptions); } - /// + /// Streams alarm feed messages from the gateway, forwarding an explicit cancellation token to the stream reader. + /// The stream alarms request. + /// gRPC call options. + /// Token to cancel the streaming enumeration. + /// An async enumerable of alarm feed messages. public async IAsyncEnumerable StreamAlarmsAsync( StreamAlarmsRequest request, CallOptions callOptions, diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/IGalaxyRepositoryClientTransport.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/IGalaxyRepositoryClientTransport.cs index ddc5dcc..e9348c3 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/IGalaxyRepositoryClientTransport.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/IGalaxyRepositoryClientTransport.cs @@ -15,6 +15,7 @@ internal interface IGalaxyRepositoryClientTransport /// Tests the connection to the Galaxy Repository server. /// The test connection request. /// gRPC call options (timeout, cancellation, etc.). + /// A task that resolves to the test connection reply. Task TestConnectionAsync( TestConnectionRequest request, CallOptions callOptions); @@ -22,6 +23,7 @@ internal interface IGalaxyRepositoryClientTransport /// Gets the last deploy time from the Galaxy Repository server. /// The get last deploy time request. /// gRPC call options (timeout, cancellation, etc.). + /// A task that resolves to the last deploy time reply. Task GetLastDeployTimeAsync( GetLastDeployTimeRequest request, CallOptions callOptions); @@ -29,6 +31,7 @@ internal interface IGalaxyRepositoryClientTransport /// Discovers the object hierarchy in the Galaxy Repository. /// The discover hierarchy request. /// gRPC call options (timeout, cancellation, etc.). + /// A task that resolves to the hierarchy discovery reply. Task DiscoverHierarchyAsync( DiscoverHierarchyRequest request, CallOptions callOptions); @@ -36,6 +39,7 @@ internal interface IGalaxyRepositoryClientTransport /// Returns direct children of a parent in the Galaxy hierarchy. /// The browse children request. /// gRPC call options (timeout, cancellation, etc.). + /// A task that resolves to the browse children reply. Task BrowseChildrenAsync( BrowseChildrenRequest request, CallOptions callOptions); @@ -43,6 +47,7 @@ internal interface IGalaxyRepositoryClientTransport /// Watches for deployment events from the Galaxy Repository server. /// The watch deploy events request. /// gRPC call options (timeout, cancellation, etc.). + /// An async enumerable of deploy events. IAsyncEnumerable WatchDeployEventsAsync( WatchDeployEventsRequest request, CallOptions callOptions); diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/LazyBrowseNode.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/LazyBrowseNode.cs index 360f4f5..b13cdcf 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/LazyBrowseNode.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/LazyBrowseNode.cs @@ -16,6 +16,11 @@ public sealed class LazyBrowseNode private readonly SemaphoreSlim _expandLock = new(1, 1); private bool _isExpanded; + /// Initializes a new instance of . + /// The repository client used to fetch children. + /// The underlying Galaxy object for this node. + /// True when the server reports the node has at least one matching descendant. + /// Options controlling child browse behavior. internal LazyBrowseNode( GalaxyRepositoryClient client, GalaxyObject @object, @@ -49,6 +54,7 @@ public sealed class LazyBrowseNode /// (after the first completes) return immediately. /// /// Token to observe for cancellation. + /// A task that represents the asynchronous operation. public async Task ExpandAsync(CancellationToken cancellationToken = default) { if (_isExpanded) diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxCommandReplyExtensions.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxCommandReplyExtensions.cs index ef111df..7546d7d 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxCommandReplyExtensions.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxCommandReplyExtensions.cs @@ -7,6 +7,7 @@ public static class MxCommandReplyExtensions { /// Validates that the reply has a successful protocol status (Ok or MxAccessFailure), throwing a gateway exception if not. /// The command reply to check. + /// The same for fluent chaining when validation passes. public static MxCommandReply EnsureProtocolSuccess(this MxCommandReply reply) { ArgumentNullException.ThrowIfNull(reply); @@ -24,6 +25,7 @@ public static class MxCommandReplyExtensions /// Validates that the reply indicates MXAccess success (no HResult or status failures), throwing MxAccessException if not. /// The command reply to check. + /// The same for fluent chaining when validation passes. public static MxCommandReply EnsureMxAccessSuccess(this MxCommandReply reply) { ArgumentNullException.ThrowIfNull(reply); diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxGatewayClient.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxGatewayClient.cs index 6d9ab58..a70ff5b 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxGatewayClient.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxGatewayClient.cs @@ -249,6 +249,7 @@ public sealed class MxGatewayClient : IAsyncDisposable /// /// Disposes the client and releases all resources. /// + /// A task that represents the asynchronous dispose operation. public ValueTask DisposeAsync() { if (_disposed) @@ -318,6 +319,9 @@ public sealed class MxGatewayClient : IAsyncDisposable private static HttpMessageHandler CreateHttpHandler(MxGatewayClientOptions options) => CreateHttpHandlerForTests(options); + /// Creates an configured from the provided options for test use. + /// Client options used to configure TLS and timeouts. + /// The configured HTTP message handler. internal static SocketsHttpHandler CreateHttpHandlerForTests(MxGatewayClientOptions options) { SocketsHttpHandler handler = new() diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxGatewayClientRetryPolicy.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxGatewayClientRetryPolicy.cs index ff4ff7a..757df75 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxGatewayClientRetryPolicy.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxGatewayClientRetryPolicy.cs @@ -12,6 +12,7 @@ internal static class MxGatewayClientRetryPolicy /// Creates a Polly ResiliencePipeline that retries transient gRPC failures with exponential backoff. /// Retry configuration (max attempts, delay bounds, jitter). /// Optional logger for retry diagnostics. + /// A configured with exponential-backoff retry. public static ResiliencePipeline Create( MxGatewayClientRetryOptions options, ILogger? logger) @@ -42,6 +43,7 @@ internal static class MxGatewayClientRetryPolicy /// Returns whether a command kind is eligible for automatic retry on transient failures. /// The command kind to check. + /// if the command kind is safe to retry; otherwise . public static bool IsRetryableCommand(MxCommandKind kind) { return kind is MxCommandKind.Ping diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxGatewaySession.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxGatewaySession.cs index 158589c..2caf220 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxGatewaySession.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxGatewaySession.cs @@ -211,6 +211,7 @@ public sealed class MxGatewaySession : IAsyncDisposable /// The ServerHandle from register. /// The ItemHandle from add-item. /// Cancellation token. + /// A task that represents the asynchronous operation. public async Task AdviseAsync( int serverHandle, int itemHandle, @@ -252,6 +253,7 @@ public sealed class MxGatewaySession : IAsyncDisposable /// The ServerHandle from register. /// The ItemHandle from add-item. /// Cancellation token. + /// A task that represents the asynchronous operation. public async Task UnAdviseAsync( int serverHandle, int itemHandle, @@ -293,6 +295,7 @@ public sealed class MxGatewaySession : IAsyncDisposable /// The ServerHandle from register. /// The ItemHandle from add-item. /// Cancellation token. + /// A task that represents the asynchronous operation. public async Task RemoveItemAsync( int serverHandle, int itemHandle, @@ -675,6 +678,7 @@ public sealed class MxGatewaySession : IAsyncDisposable /// The value to write. /// User ID context for the write. /// Cancellation token. + /// A task that represents the asynchronous operation. public async Task WriteAsync( int serverHandle, int itemHandle, @@ -729,6 +733,7 @@ public sealed class MxGatewaySession : IAsyncDisposable /// The timestamp to write with the value. /// User ID context for the write. /// Cancellation token. + /// A task that represents the asynchronous operation. public async Task Write2Async( int serverHandle, int itemHandle, @@ -821,6 +826,7 @@ public sealed class MxGatewaySession : IAsyncDisposable /// /// Closes the session and releases resources. /// + /// A task that represents the asynchronous operation. public async ValueTask DisposeAsync() { await CloseAsync().ConfigureAwait(false); diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxStatusProxyExtensions.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxStatusProxyExtensions.cs index 5441431..88cd6ad 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxStatusProxyExtensions.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxStatusProxyExtensions.cs @@ -7,6 +7,7 @@ public static class MxStatusProxyExtensions { /// Returns whether the status indicates success (success flag set and category is Ok). /// The status to check. + /// true if the status is successful; false otherwise. public static bool IsSuccess(this MxStatusProxy status) { ArgumentNullException.ThrowIfNull(status); @@ -17,6 +18,7 @@ public static class MxStatusProxyExtensions /// Returns a formatted summary of the status for diagnostic output. /// The status to summarize. + /// A human-readable string combining category, source, detail, and diagnostic text. public static string ToDiagnosticSummary(this MxStatusProxy status) { ArgumentNullException.ThrowIfNull(status); diff --git a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxValueExtensions.cs b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxValueExtensions.cs index 4f047bf..77c9258 100644 --- a/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxValueExtensions.cs +++ b/clients/dotnet/ZB.MOM.WW.MxGateway.Client/MxValueExtensions.cs @@ -14,6 +14,7 @@ public static class MxValueExtensions /// Converts a boolean value to an MxValue with MxDataType.Boolean. /// /// Scalar boolean value to wrap. + /// An with MxDataType.Boolean. public static MxValue ToMxValue(this bool value) { return new MxValue @@ -28,6 +29,7 @@ public static class MxValueExtensions /// Converts a 32-bit integer value to an MxValue with MxDataType.Integer. /// /// 32-bit integer value to wrap. + /// An with MxDataType.Integer. public static MxValue ToMxValue(this int value) { return new MxValue @@ -42,6 +44,7 @@ public static class MxValueExtensions /// Converts a 64-bit integer value to an MxValue with MxDataType.Integer. /// /// 64-bit integer value to wrap. + /// An with MxDataType.Integer. public static MxValue ToMxValue(this long value) { return new MxValue @@ -56,6 +59,7 @@ public static class MxValueExtensions /// Converts a single-precision floating-point value to an MxValue with MxDataType.Float. /// /// Single-precision floating-point value to wrap. + /// An with MxDataType.Float. public static MxValue ToMxValue(this float value) { return new MxValue @@ -70,6 +74,7 @@ public static class MxValueExtensions /// Converts a double-precision floating-point value to an MxValue with MxDataType.Double. /// /// Double-precision floating-point value to wrap. + /// An with MxDataType.Double. public static MxValue ToMxValue(this double value) { return new MxValue @@ -84,6 +89,7 @@ public static class MxValueExtensions /// Converts a string value to an MxValue with MxDataType.String. /// /// String value to wrap. + /// An with MxDataType.String. public static MxValue ToMxValue(this string value) { ArgumentNullException.ThrowIfNull(value); @@ -100,6 +106,7 @@ public static class MxValueExtensions /// Converts a DateTimeOffset value to an MxValue with MxDataType.Time. /// /// DateTimeOffset value to wrap. + /// An with MxDataType.Time. public static MxValue ToMxValue(this DateTimeOffset value) { return new MxValue @@ -114,6 +121,7 @@ public static class MxValueExtensions /// Converts a DateTime value to an MxValue with MxDataType.Time. /// /// DateTime value to wrap. + /// An with MxDataType.Time. public static MxValue ToMxValue(this DateTime value) { return new DateTimeOffset( @@ -127,6 +135,7 @@ public static class MxValueExtensions /// Converts a boolean array to an MxValue with MxDataType.Boolean. /// /// Array of boolean values to wrap. + /// An with MxDataType.Boolean and an array payload. public static MxValue ToMxValue(this IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); @@ -145,6 +154,7 @@ public static class MxValueExtensions /// Converts a 32-bit integer array to an MxValue with MxDataType.Integer. /// /// Array of 32-bit integer values to wrap. + /// An with MxDataType.Integer and an array payload. public static MxValue ToMxValue(this IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); @@ -163,6 +173,7 @@ public static class MxValueExtensions /// Converts a 64-bit integer array to an MxValue with MxDataType.Integer. /// /// Array of 64-bit integer values to wrap. + /// An with MxDataType.Integer and an array payload. public static MxValue ToMxValue(this IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); @@ -181,6 +192,7 @@ public static class MxValueExtensions /// Converts a single-precision floating-point array to an MxValue with MxDataType.Float. /// /// Array of single-precision floating-point values to wrap. + /// An with MxDataType.Float and an array payload. public static MxValue ToMxValue(this IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); @@ -199,6 +211,7 @@ public static class MxValueExtensions /// Converts a double-precision floating-point array to an MxValue with MxDataType.Double. /// /// Array of double-precision floating-point values to wrap. + /// An with MxDataType.Double and an array payload. public static MxValue ToMxValue(this IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); @@ -217,6 +230,7 @@ public static class MxValueExtensions /// Converts a string array to an MxValue with MxDataType.String. /// /// Array of string values to wrap. + /// An with MxDataType.String and an array payload. public static MxValue ToMxValue(this IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); @@ -235,6 +249,7 @@ public static class MxValueExtensions /// Converts a DateTimeOffset array to an MxValue with MxDataType.Time. /// /// Array of DateTimeOffset values to wrap. + /// An with MxDataType.Time and an array payload. public static MxValue ToMxValue(this IReadOnlyList values) { ArgumentNullException.ThrowIfNull(values); @@ -253,6 +268,7 @@ public static class MxValueExtensions /// Gets the projection kind (field name) of the given MxValue's current oneof value. /// /// The MxValue whose oneof projection kind is returned. + /// The JSON field name of the active oneof case, or "nullValue"/"unspecified" for null/unset values. public static string GetProjectionKind(this MxValue value) { ArgumentNullException.ThrowIfNull(value); @@ -276,6 +292,7 @@ public static class MxValueExtensions /// Converts an MxValue to a CLR object; returns the boxed value or null for null MxValues. /// /// The MxValue to convert. + /// The boxed CLR value, or null if the MxValue represents a null. public static object? ToClrValue(this MxValue value) { ArgumentNullException.ThrowIfNull(value); @@ -299,6 +316,7 @@ public static class MxValueExtensions /// Converts an MxArray to a CLR array; returns null if the array does not have a known element type. /// /// The MxArray to convert. + /// A CLR array of the appropriate element type, or null for unknown element types. public static object? ToClrArrayValue(this MxArray array) { ArgumentNullException.ThrowIfNull(array); @@ -328,6 +346,7 @@ public static class MxValueExtensions /// Variant type string (e.g., "VT_BSTR"). /// Diagnostic string describing the raw value. /// Optional MXAccess data type override. + /// An with MxDataType.Unknown and the raw byte payload. public static MxValue ToRawMxValue( byte[] value, string variantType, diff --git a/src/ZB.MOM.WW.MxGateway.IntegrationTests/DashboardLdapLiveTests.cs b/src/ZB.MOM.WW.MxGateway.IntegrationTests/DashboardLdapLiveTests.cs index 0d2d39a..46ddd0e 100644 --- a/src/ZB.MOM.WW.MxGateway.IntegrationTests/DashboardLdapLiveTests.cs +++ b/src/ZB.MOM.WW.MxGateway.IntegrationTests/DashboardLdapLiveTests.cs @@ -14,6 +14,7 @@ namespace ZB.MOM.WW.MxGateway.IntegrationTests; public sealed class DashboardLdapLiveTests { /// Verifies that an admin user in the GwAdmin group authenticates successfully. + /// A task that represents the asynchronous operation. [LiveLdapFact] public async Task AuthenticateAsync_AdminInGwAdminGroup_Succeeds() { @@ -42,6 +43,7 @@ public sealed class DashboardLdapLiveTests } /// Verifies that a readonly user without GwAdmin group fails to authenticate. + /// A task that represents the asynchronous operation. [LiveLdapFact] public async Task AuthenticateAsync_ReadOnlyUserMissingGwAdminGroup_Fails() { @@ -58,6 +60,7 @@ public sealed class DashboardLdapLiveTests } /// Verifies that authentication with wrong password fails without leaking the password. + /// A task that represents the asynchronous operation. [LiveLdapFact] public async Task AuthenticateAsync_AdminWithWrongPassword_FailsWithoutLeakingPassword() { @@ -77,6 +80,7 @@ public sealed class DashboardLdapLiveTests } /// Verifies that authentication with unknown username fails. + /// A task that represents the asynchronous operation. [LiveLdapFact] public async Task AuthenticateAsync_UnknownUsername_Fails() { @@ -94,6 +98,7 @@ public sealed class DashboardLdapLiveTests } /// Verifies that authentication fails gracefully when the server is unreachable. + /// A task that represents the asynchronous operation. [LiveLdapFact] public async Task AuthenticateAsync_ServerUnreachable_FailsWithoutThrowing() { diff --git a/src/ZB.MOM.WW.MxGateway.IntegrationTests/Galaxy/GalaxyRepositoryLiveTests.cs b/src/ZB.MOM.WW.MxGateway.IntegrationTests/Galaxy/GalaxyRepositoryLiveTests.cs index 9ffc4bd..d727943 100644 --- a/src/ZB.MOM.WW.MxGateway.IntegrationTests/Galaxy/GalaxyRepositoryLiveTests.cs +++ b/src/ZB.MOM.WW.MxGateway.IntegrationTests/Galaxy/GalaxyRepositoryLiveTests.cs @@ -7,6 +7,7 @@ namespace ZB.MOM.WW.MxGateway.IntegrationTests.Galaxy; public sealed class GalaxyRepositoryLiveTests { /// Verifies that the Galaxy Repository can establish a live connection to the ZB database. + /// A task that represents the asynchronous operation. [LiveGalaxyRepositoryFact] public async Task TestConnection_AgainstZb_Succeeds() { @@ -18,6 +19,7 @@ public sealed class GalaxyRepositoryLiveTests } /// Verifies that the last deploy time can be retrieved from the ZB database. + /// A task that represents the asynchronous operation. [LiveGalaxyRepositoryFact] public async Task GetLastDeployTime_AgainstZb_ReturnsTimestamp() { @@ -29,6 +31,7 @@ public sealed class GalaxyRepositoryLiveTests } /// Verifies that the hierarchy can be retrieved from the ZB database. + /// A task that represents the asynchronous operation. [LiveGalaxyRepositoryFact] public async Task GetHierarchy_AgainstZb_ReturnsObjects() { @@ -46,6 +49,7 @@ public sealed class GalaxyRepositoryLiveTests } /// Verifies that object attributes can be retrieved from the ZB database. + /// A task that represents the asynchronous operation. [LiveGalaxyRepositoryFact] public async Task GetAttributes_AgainstZb_ReturnsAtLeastOneAttribute() { diff --git a/src/ZB.MOM.WW.MxGateway.IntegrationTests/WorkerLiveMxAccessSmokeTests.cs b/src/ZB.MOM.WW.MxGateway.IntegrationTests/WorkerLiveMxAccessSmokeTests.cs index 4464c52..aa38648 100644 --- a/src/ZB.MOM.WW.MxGateway.IntegrationTests/WorkerLiveMxAccessSmokeTests.cs +++ b/src/ZB.MOM.WW.MxGateway.IntegrationTests/WorkerLiveMxAccessSmokeTests.cs @@ -30,6 +30,7 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output) /// /// Verifies that a gateway session can register, add item, advise, and stream events from live MXAccess. /// + /// A task that represents the asynchronous operation. [LiveMxAccessFact] public async Task GatewaySession_WithLiveWorker_RegistersAdvisesStreamsDataAndCloses() { @@ -119,6 +120,7 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output) /// and that the worker emits a matching event /// — the proof of round-trip the cross-language client e2e runner relies on. /// + /// A task that represents the asynchronous operation. [LiveMxAccessFact] public async Task GatewaySession_WithLiveWorker_WritesValueToAdvisedItem() { @@ -235,6 +237,7 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output) /// Verifies that an AddItem against an invalid server handle surfaces the MXAccess failure /// without faulting the gateway transport, exercising the invalid-handle parity path. /// + /// A task that represents the asynchronous operation. [LiveMxAccessFact] public async Task GatewaySession_WithLiveWorker_InvalidHandleCommand_SurfacesFailureWithoutTransportFault() { @@ -293,6 +296,7 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output) /// OnDataChange events for the un-advised item. Exercises the lifecycle-ordering /// parity CLAUDE.md singles out as a "do not synthesize" rule. /// + /// A task that represents the asynchronous operation. [LiveMxAccessFact] public async Task GatewaySession_WithLiveWorker_UnadviseRemoveItemUnregister_TeardownOrderingParity() { @@ -437,6 +441,7 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output) /// parity surface the gateway must not "fix" — the test asserts the reply kind and /// protocol status, not a fabricated outcome. /// + /// A task that represents the asynchronous operation. [LiveMxAccessFact] public async Task GatewaySession_WithLiveWorker_WriteSecured_AuthenticatedRoundTripParity() { @@ -568,6 +573,7 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output) /// must observe the abnormal exit, transition the session, and surface a non-empty /// fault description rather than hanging or crashing. /// + /// A task that represents the asynchronous operation. [LiveMxAccessFact] public async Task GatewaySession_WithLiveWorker_AbnormalWorkerExit_MarksSessionFaulted() { @@ -1114,6 +1120,7 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output) /// /// The session identifier. /// The session if found; otherwise null. + /// True if the session was found; otherwise false. public bool TryGetSession(string sessionId, [MaybeNullWhen(false)] out GatewaySession session) { return _registry.TryGet(sessionId, out session); @@ -1122,6 +1129,7 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output) /// /// Disposes the fixture resources and closes all sessions. /// + /// A task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { foreach (GatewaySession session in _registry.Snapshot()) @@ -1192,6 +1200,7 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output) /// Records the message and signals any pending waiter. /// /// The message to write. + /// A task that represents the asynchronous operation. public Task WriteAsync(T message) { lock (syncRoot) @@ -1374,7 +1383,9 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output) return workerProcess; } - /// + /// Waits for all recorded worker processes to exit within the specified timeout. + /// Maximum time to wait for each process to exit. + /// A task that represents the asynchronous operation. public async Task WaitForProcessesAsync(TimeSpan timeout) { foreach (TestWorkerProcess process in processes) @@ -1454,7 +1465,7 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output) process.Kill(entireProcessTree); } - /// + /// Releases the wrapped process resources. public void Dispose() { process.Dispose(); @@ -1466,13 +1477,15 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output) /// private sealed class TestOutputLoggerProvider(ITestOutputHelper output) : ILoggerProvider { - /// + /// Creates a logger that writes to the test output helper for the given category. + /// The logger category name. + /// A logger that forwards to the test output helper. public ILogger CreateLogger(string categoryName) { return new TestOutputLogger(output, categoryName); } - /// + /// Releases resources held by the provider (no-op for this test double). public void Dispose() { } @@ -1485,20 +1498,31 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output) ITestOutputHelper output, string categoryName) : ILogger { - /// + /// Begins a log scope; returns null as this test logger does not support scopes. + /// The state object for the scope. + /// The type of the state object. + /// Always null. public IDisposable? BeginScope(TState state) where TState : notnull { return null; } - /// + /// Returns true for log levels at or above . + /// The log level to check. + /// True if the log level is enabled. public bool IsEnabled(LogLevel logLevel) { return logLevel >= LogLevel.Information; } - /// + /// Writes a log entry to the test output helper. + /// The log level. + /// The event identifier. + /// The state object to log. + /// Optional exception associated with the log entry. + /// Function to format the state and exception into a string. + /// The type of the state object. public void Log( LogLevel logLevel, EventId eventId, diff --git a/src/ZB.MOM.WW.MxGateway.Server/Alarms/GatewayAlarmMonitor.cs b/src/ZB.MOM.WW.MxGateway.Server/Alarms/GatewayAlarmMonitor.cs index 30461c9..ef0101a 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Alarms/GatewayAlarmMonitor.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Alarms/GatewayAlarmMonitor.cs @@ -688,6 +688,7 @@ public sealed class GatewayAlarmMonitor : BackgroundService, IGatewayAlarmServic /// Determines whether the alarm reference matches this subscriber's filter. /// The alarm reference to match. + /// True if the reference starts with this subscriber's prefix or no prefix is set. public bool Matches(string reference) { return prefix.Length == 0 || reference.StartsWith(prefix, StringComparison.Ordinal); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Alarms/IGatewayAlarmService.cs b/src/ZB.MOM.WW.MxGateway.Server/Alarms/IGatewayAlarmService.cs index 268f7a7..69de41b 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Alarms/IGatewayAlarmService.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Alarms/IGatewayAlarmService.cs @@ -46,6 +46,7 @@ public interface IGatewayAlarmService /// /// Optional alarm-reference prefix scoping the feed. /// Token that ends the subscription. + /// An async enumerable of alarm feed messages. IAsyncEnumerable StreamAsync( string? alarmFilterPrefix, CancellationToken cancellationToken); @@ -57,6 +58,7 @@ public interface IGatewayAlarmService /// /// The acknowledge request. /// Token to cancel the call. + /// A task that resolves to the acknowledge reply. Task AcknowledgeAsync( AcknowledgeAlarmRequest request, CancellationToken cancellationToken); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptionsValidator.cs b/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptionsValidator.cs index c228a5c..9109d8c 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptionsValidator.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Configuration/GatewayOptionsValidator.cs @@ -9,11 +9,7 @@ public sealed class GatewayOptionsValidator : OptionsValidatorBase - /// Validates gateway configuration options. - /// - /// The accumulator to record failures on. - /// Gateway options to validate. + /// protected override void Validate(ValidationBuilder builder, GatewayOptions options) { ValidateAuthentication(options.Authentication, builder); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Configuration/IGatewayConfigurationProvider.cs b/src/ZB.MOM.WW.MxGateway.Server/Configuration/IGatewayConfigurationProvider.cs index d4a1275..d47000e 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Configuration/IGatewayConfigurationProvider.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Configuration/IGatewayConfigurationProvider.cs @@ -8,5 +8,6 @@ public interface IGatewayConfigurationProvider /// /// Returns the validated and effective gateway configuration. /// + /// The with validated defaults applied. EffectiveGatewayConfiguration GetEffectiveConfiguration(); } diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/Components/DashboardPageBase.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/Components/DashboardPageBase.cs index f323bd4..28fc160 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/Components/DashboardPageBase.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/Components/DashboardPageBase.cs @@ -38,7 +38,8 @@ public abstract class DashboardPageBase : ComponentBase, IAsyncDisposable await ConnectHubAsync().ConfigureAwait(false); } - /// + /// Disposes the SignalR hub connection and suppresses finalization. + /// A task that represents the asynchronous operation. public async ValueTask DisposeAsync() { if (_hub is not null) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardApiKeyAuthorization.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardApiKeyAuthorization.cs index cc81219..8f8532f 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardApiKeyAuthorization.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardApiKeyAuthorization.cs @@ -6,6 +6,7 @@ public sealed class DashboardApiKeyAuthorization { /// Determines whether the user can manage API keys. /// The authenticated user principal. + /// True if the user is an authenticated admin; otherwise false. public bool CanManage(ClaimsPrincipal user) { if (user.Identity?.IsAuthenticated != true) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardApiKeyManagementService.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardApiKeyManagementService.cs index 86a241d..1963d7a 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardApiKeyManagementService.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardApiKeyManagementService.cs @@ -20,17 +20,13 @@ public sealed class DashboardApiKeyManagementService( private const string UnauthorizedMessage = "Sign in with an authorized LDAP account to manage API keys."; private const string PepperUnavailableMarker = "pepper unavailable"; - /// Determines whether the user can manage API keys. - /// The authenticated user principal. + /// public bool CanManage(ClaimsPrincipal user) { return authorization.CanManage(user); } - /// Creates an API key asynchronously. - /// The authenticated user principal. - /// The request payload. - /// Token to observe for cancellation. + /// public async Task CreateAsync( ClaimsPrincipal user, DashboardApiKeyManagementRequest request, @@ -82,10 +78,7 @@ public sealed class DashboardApiKeyManagementService( } } - /// Revokes an API key asynchronously. - /// The authenticated user principal. - /// The API key identifier. - /// Token to observe for cancellation. + /// public async Task RevokeAsync( ClaimsPrincipal user, string keyId, @@ -120,10 +113,7 @@ public sealed class DashboardApiKeyManagementService( : DashboardApiKeyManagementResult.Fail("API key was not found or is already revoked."); } - /// Rotates an API key secret asynchronously. - /// The authenticated user principal. - /// The API key identifier. - /// Token to observe for cancellation. + /// public async Task RotateAsync( ClaimsPrincipal user, string keyId, @@ -170,10 +160,7 @@ public sealed class DashboardApiKeyManagementService( } } - /// Deletes a revoked API key asynchronously. - /// The authenticated user principal. - /// The API key identifier. - /// Token to observe for cancellation. + /// public async Task DeleteAsync( ClaimsPrincipal user, string keyId, diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardAuthenticationResult.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardAuthenticationResult.cs index b656c79..e21faa9 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardAuthenticationResult.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardAuthenticationResult.cs @@ -23,6 +23,7 @@ public sealed record DashboardAuthenticationResult( /// Creates a successful authentication result. /// /// Authenticated principal. + /// A successful wrapping the principal. public static DashboardAuthenticationResult Success(ClaimsPrincipal principal) { return new DashboardAuthenticationResult(true, principal, null); @@ -32,6 +33,7 @@ public sealed record DashboardAuthenticationResult( /// Creates a failed authentication result. /// /// Diagnostic message describing the failure. + /// A failed with the given message. public static DashboardAuthenticationResult Fail(string failureMessage) { return new DashboardAuthenticationResult(false, null, failureMessage); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardConnectionStringDisplay.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardConnectionStringDisplay.cs index b0f1f2d..2e54879 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardConnectionStringDisplay.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardConnectionStringDisplay.cs @@ -6,6 +6,7 @@ public static class DashboardConnectionStringDisplay { /// Returns a sanitized Galaxy Repository connection string for display. /// The connection string to sanitize. + /// A sanitized connection string with credentials removed, or "[invalid connection string]" if parsing fails. public static string GalaxyRepositoryConnectionString(string connectionString) { try diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardGalaxyProjector.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardGalaxyProjector.cs index a87e0c4..693ee90 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardGalaxyProjector.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardGalaxyProjector.cs @@ -7,6 +7,7 @@ internal static class DashboardGalaxyProjector { /// Projects the cache entry to a dashboard Galaxy summary. /// The Galaxy hierarchy cache entry. + /// The precomputed from the cache entry. public static DashboardGalaxySummary Project(GalaxyHierarchyCacheEntry entry) { return entry.DashboardSummary; diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardGroupRoleMapper.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardGroupRoleMapper.cs index 946c7a1..72ea782 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardGroupRoleMapper.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardGroupRoleMapper.cs @@ -17,7 +17,10 @@ namespace ZB.MOM.WW.MxGateway.Server.Dashboard; public sealed class DashboardGroupRoleMapper(IOptions options) : IGroupRoleMapper { - /// + /// Maps LDAP group memberships to dashboard roles using the configured group-to-role rules. + /// The list of LDAP group names or distinguished names for the authenticated user. + /// Token to cancel the asynchronous operation. + /// A task that resolves to the role mapping for the supplied groups. public Task> MapAsync( IReadOnlyList groups, CancellationToken ct) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardGroupRoleMapping.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardGroupRoleMapping.cs index 2fa1029..4a7a9e2 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardGroupRoleMapping.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardGroupRoleMapping.cs @@ -16,6 +16,7 @@ internal static class DashboardGroupRoleMapping /// /// The collection of LDAP groups the user belongs to. /// The mapping from group names to dashboard role names. + /// The distinct set of dashboard roles matched from the user's groups. internal static IReadOnlyList MapGroupsToRoles( IEnumerable groups, IReadOnlyDictionary groupToRole) @@ -61,6 +62,7 @@ internal static class DashboardGroupRoleMapping /// Extracts the first RDN value from a distinguished name. /// The LDAP distinguished name. + /// The value portion of the first RDN component, or the full string if no = is found. internal static string ExtractFirstRdnValue(string distinguishedName) { int equalsIndex = distinguishedName.IndexOf('='); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardLiveDataService.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardLiveDataService.cs index 9e7b4b2..86d47fd 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardLiveDataService.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardLiveDataService.cs @@ -192,7 +192,8 @@ public sealed class DashboardLiveDataService : IDashboardLiveDataService, IAsync } } - /// + /// Releases resources and closes the associated gateway session. + /// A task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { if (_disposed) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardServiceCollectionExtensions.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardServiceCollectionExtensions.cs index 806fe41..7982592 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardServiceCollectionExtensions.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardServiceCollectionExtensions.cs @@ -23,6 +23,7 @@ public static class DashboardServiceCollectionExtensions /// Application configuration, used to bind the shared LDAP provider's options /// from the MxGateway:Ldap section. /// + /// The collection for chaining. public static IServiceCollection AddGatewayDashboard( this IServiceCollection services, IConfiguration configuration) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardSessionAdminResult.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardSessionAdminResult.cs index 136e92a..53fa124 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardSessionAdminResult.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardSessionAdminResult.cs @@ -6,6 +6,7 @@ public sealed record DashboardSessionAdminResult( { /// Creates a successful result with the given message. /// The result message. + /// A with Succeeded set to true. public static DashboardSessionAdminResult Success(string message) { return new DashboardSessionAdminResult(true, message); @@ -13,6 +14,7 @@ public sealed record DashboardSessionAdminResult( /// Creates a failed result with the given message. /// The result message. + /// A with Succeeded set to false. public static DashboardSessionAdminResult Fail(string message) { return new DashboardSessionAdminResult(false, message); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardSnapshotService.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardSnapshotService.cs index 290879c..28dcae5 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardSnapshotService.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardSnapshotService.cs @@ -65,10 +65,7 @@ public sealed class DashboardSnapshotService : IDashboardSnapshotService _logger = logger ?? NullLogger.Instance; } - /// - /// Gets a current dashboard snapshot of gateway state. - /// - /// Dashboard snapshot. + /// public DashboardSnapshot GetSnapshot() { DateTimeOffset generatedAt = _timeProvider.GetUtcNow(); @@ -100,11 +97,7 @@ public sealed class DashboardSnapshotService : IDashboardSnapshotService Galaxy: DashboardGalaxyProjector.Project(_galaxyHierarchyCache.Current)); } - /// - /// Watches dashboard snapshots at regular intervals asynchronously. - /// - /// Cancellation token. - /// Async enumerable of dashboard snapshots. + /// public async IAsyncEnumerable WatchSnapshotsAsync( [EnumeratorCancellation] CancellationToken cancellationToken) { diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/HubTokenService.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/HubTokenService.cs index 52bd9af..3a4f3a6 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/HubTokenService.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/HubTokenService.cs @@ -40,6 +40,7 @@ public sealed class HubTokenService /// Issues a bearer token carrying the user's identity and roles. /// The claims principal representing the user. + /// The time-limited bearer token string. public string Issue(ClaimsPrincipal user) { ArgumentNullException.ThrowIfNull(user); @@ -52,6 +53,7 @@ public sealed class HubTokenService /// Validates a token and returns the equivalent claims principal; null when invalid or expired. /// The token string to validate. + /// The claims principal if the token is valid, or null if invalid or expired. public ClaimsPrincipal? Validate(string? token) { if (string.IsNullOrEmpty(token)) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/Hubs/DashboardEventBroadcaster.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/Hubs/DashboardEventBroadcaster.cs index 2e63b08..91c0a3a 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/Hubs/DashboardEventBroadcaster.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/Hubs/DashboardEventBroadcaster.cs @@ -14,9 +14,7 @@ public sealed class DashboardEventBroadcaster( IHubContext hubContext, ILogger logger) : IDashboardEventBroadcaster { - /// Publishes an MX event to connected dashboard clients. - /// The session identifier. - /// The MX event to publish. + /// public void Publish(string sessionId, MxEvent mxEvent) { if (string.IsNullOrEmpty(sessionId) || mxEvent is null) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/Hubs/EventsHub.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/Hubs/EventsHub.cs index 382dcad..2db9dfd 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/Hubs/EventsHub.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/Hubs/EventsHub.cs @@ -49,6 +49,7 @@ public sealed class EventsHub : Hub /// dedicated authorization policy applied to the hub method itself. /// /// Session id to subscribe the caller to. + /// A task that represents the asynchronous operation. public Task SubscribeSession(string sessionId) { if (string.IsNullOrWhiteSpace(sessionId)) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/IDashboardAuthenticator.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/IDashboardAuthenticator.cs index 5496f4d..546fd50 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/IDashboardAuthenticator.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/IDashboardAuthenticator.cs @@ -11,6 +11,7 @@ public interface IDashboardAuthenticator /// Username to authenticate. /// Password to authenticate. /// Token to cancel the asynchronous operation. + /// A task that resolves to the authentication result. Task AuthenticateAsync( string? username, string? password, diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/IDashboardBrowseService.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/IDashboardBrowseService.cs index 53becb9..3693d27 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/IDashboardBrowseService.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/IDashboardBrowseService.cs @@ -12,11 +12,13 @@ public interface IDashboardBrowseService { /// Returns root browse nodes (objects with no parent). /// Filter arguments forwarded to the projector. + /// The root-level browse result. BrowseLevelResult GetRoots(BrowseFilterArgs filter); /// Returns the direct children of the given parent gobject id. /// The Galaxy gobject id of the parent to expand. /// Filter arguments forwarded to the projector. + /// The children browse result for the specified parent. BrowseLevelResult GetChildren(int parentGobjectId, BrowseFilterArgs filter); /// Current Galaxy cache sequence. Bumps after each successful refresh. diff --git a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/IDashboardSnapshotService.cs b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/IDashboardSnapshotService.cs index ca33819..1156d12 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Dashboard/IDashboardSnapshotService.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Dashboard/IDashboardSnapshotService.cs @@ -8,11 +8,13 @@ public interface IDashboardSnapshotService /// /// Gets the current dashboard snapshot. /// + /// The most recent . DashboardSnapshot GetSnapshot(); /// /// Watches for changes to the dashboard state as an async enumerable. /// /// Token to cancel the asynchronous operation. + /// An async sequence of values as state changes. IAsyncEnumerable WatchSnapshotsAsync(CancellationToken cancellationToken); } diff --git a/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/AuthStoreHealthCheck.cs b/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/AuthStoreHealthCheck.cs index 30766a5..5c452c9 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/AuthStoreHealthCheck.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/AuthStoreHealthCheck.cs @@ -12,9 +12,15 @@ public sealed class AuthStoreHealthCheck : IHealthCheck { private readonly AuthSqliteConnectionFactory _connectionFactory; + /// Initializes a new instance of with the given connection factory. + /// Factory for opening SQLite connections to the auth store. public AuthStoreHealthCheck(AuthSqliteConnectionFactory connectionFactory) => _connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory)); + /// Runs a lightweight connectivity probe against the SQLite authentication store. + /// Health check context supplied by the framework. + /// Token to cancel the asynchronous operation. + /// A task that resolves to the health check result. public async Task CheckHealthAsync( HealthCheckContext context, CancellationToken cancellationToken = default) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayLogRedactor.cs b/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayLogRedactor.cs index d04d2aa..cbe01a1 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayLogRedactor.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayLogRedactor.cs @@ -19,6 +19,7 @@ public static class GatewayLogRedactor /// Determines whether a command method bears credentials. /// /// The command method name to check. + /// true if the method carries credentials; otherwise false. public static bool IsCredentialBearingCommand(string? commandMethod) { return commandMethod is not null @@ -29,6 +30,7 @@ public static class GatewayLogRedactor /// Redacts the API key secret portion of a Bearer authorization header. /// /// The authorization header value to redact. + /// The header with the secret portion replaced by , or the original if no key is detected. public static string? RedactApiKey(string? authorizationHeader) { if (string.IsNullOrWhiteSpace(authorizationHeader)) @@ -62,6 +64,7 @@ public static class GatewayLogRedactor /// Redacts the client identity if it contains an API key. /// /// The client identity string to redact. + /// The redacted identity string, or the original if no key pattern is found. public static string? RedactClientIdentity(string? clientIdentity) { if (string.IsNullOrWhiteSpace(clientIdentity)) @@ -80,6 +83,7 @@ public static class GatewayLogRedactor /// The command method name to check for credentials. /// The command value to redact. /// Whether value logging is enabled. + /// The original value when logging is enabled and the command is not credential-bearing; otherwise . public static object? RedactCommandValue( string? commandMethod, object? value, diff --git a/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayLogScope.cs b/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayLogScope.cs index c80a2d4..b85df37 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayLogScope.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayLogScope.cs @@ -8,6 +8,7 @@ public sealed record GatewayLogScope( string? ClientIdentity = null) { /// Converts the log scope to a dictionary with redacted sensitive fields. + /// A dictionary of non-null scope properties with sensitive fields redacted. public IReadOnlyDictionary ToDictionary() { Dictionary values = []; diff --git a/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayRequestLoggingMiddlewareExtensions.cs b/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayRequestLoggingMiddlewareExtensions.cs index 84a0af0..fd1b03a 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayRequestLoggingMiddlewareExtensions.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Diagnostics/GatewayRequestLoggingMiddlewareExtensions.cs @@ -19,6 +19,7 @@ public static class GatewayRequestLoggingMiddlewareExtensions /// Adds gateway request logging scope middleware that reads correlation headers and redacts sensitive data. /// Application builder. + /// The instance for chaining. public static IApplicationBuilder UseGatewayRequestLoggingScope(this IApplicationBuilder app) { ArgumentNullException.ThrowIfNull(app); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyBrowseProjector.cs b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyBrowseProjector.cs index 1768fb3..0738e7a 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyBrowseProjector.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyBrowseProjector.cs @@ -27,6 +27,7 @@ public static class GalaxyBrowseProjector /// Optional API-key browse-subtree constraints. /// Zero-based offset into the filtered child list. /// Maximum number of children to return. + /// A page of children with total count and filter signature. public static GalaxyBrowseChildrenResult ProjectChildren( GalaxyHierarchyCacheEntry entry, BrowseChildrenRequest request, @@ -71,6 +72,7 @@ public static class GalaxyBrowseProjector /// /// The Galaxy hierarchy cache entry to query. /// The browse-children request. + /// The resolved parent gobject id, or 0 for roots. public static int ResolveParentId(GalaxyHierarchyCacheEntry entry, BrowseChildrenRequest request) { switch (request.ParentCase) @@ -257,6 +259,7 @@ public static class GalaxyBrowseProjector /// The browse-children request. /// Optional API-key browse-subtree constraints. /// Resolved parent gobject id (0 for roots). + /// A hex-encoded SHA-256 prefix that uniquely identifies the filter combination. public static string ComputeFilterSignature( BrowseChildrenRequest request, IReadOnlyList? browseSubtreeGlobs, diff --git a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyDeployNotifier.cs b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyDeployNotifier.cs index f7ae0a0..d6cfee6 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyDeployNotifier.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyDeployNotifier.cs @@ -20,9 +20,7 @@ public sealed class GalaxyDeployNotifier : IGalaxyDeployNotifier private readonly ConcurrentDictionary> _subscribers = new(); private GalaxyDeployEventInfo? _latest; - /// - /// The most recent deploy event, or null if none has been published. - /// + /// public GalaxyDeployEventInfo? Latest => Volatile.Read(ref _latest); /// diff --git a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyGlobMatcher.cs b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyGlobMatcher.cs index 2ec139e..8b79e0a 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyGlobMatcher.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyGlobMatcher.cs @@ -46,6 +46,7 @@ public static class GalaxyGlobMatcher /// Determines whether a value matches a glob pattern (with * and ? wildcards). /// The value to test against the glob pattern. /// The glob pattern with * and ? wildcards. + /// if the value matches the glob pattern; otherwise . public static bool IsMatch(string value, string glob) { if (string.IsNullOrWhiteSpace(glob)) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyHierarchyCache.cs b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyHierarchyCache.cs index 8bafe52..0bbc7c0 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyHierarchyCache.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyHierarchyCache.cs @@ -54,7 +54,7 @@ public sealed class GalaxyHierarchyCache : IGalaxyHierarchyCache _snapshotStore = snapshotStore; } - /// Gets the current Galaxy hierarchy cache entry with projected status. + /// public GalaxyHierarchyCacheEntry Current { get @@ -74,9 +74,7 @@ public sealed class GalaxyHierarchyCache : IGalaxyHierarchyCache } } - /// Refreshes the Galaxy hierarchy cache if the deploy time has advanced. - /// Token to cancel the asynchronous operation. - /// Asynchronous task representing the refresh operation. + /// public async Task RefreshAsync(CancellationToken cancellationToken) { await _refreshGate.WaitAsync(cancellationToken).ConfigureAwait(false); @@ -90,9 +88,7 @@ public sealed class GalaxyHierarchyCache : IGalaxyHierarchyCache } } - /// Waits for the Galaxy hierarchy cache to complete its first load. - /// Token to cancel the asynchronous operation. - /// Asynchronous task representing the wait operation. + /// public Task WaitForFirstLoadAsync(CancellationToken cancellationToken) { return _firstLoad.Task.WaitAsync(cancellationToken); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyHierarchyProjector.cs b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyHierarchyProjector.cs index d74df56..44b788f 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyHierarchyProjector.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyHierarchyProjector.cs @@ -25,6 +25,7 @@ public static class GalaxyHierarchyProjector /// The Galaxy hierarchy cache entry. /// The discovery hierarchy request. /// Optional glob patterns to filter browse subtrees. + /// The query result containing matching objects. public static GalaxyHierarchyQueryResult Project( GalaxyHierarchyCacheEntry entry, DiscoverHierarchyRequest request, @@ -44,6 +45,7 @@ public static class GalaxyHierarchyProjector /// Optional glob patterns to filter browse subtrees. /// The zero-based offset into the result set. /// The maximum number of results to return. + /// The query result containing the requested page of matching objects. public static GalaxyHierarchyQueryResult Project( GalaxyHierarchyCacheEntry entry, DiscoverHierarchyRequest request, @@ -131,6 +133,7 @@ public static class GalaxyHierarchyProjector /// Finds an object in the hierarchy by its tag address. /// The Galaxy hierarchy cache entry. /// The tag address to search for. + /// The matching Galaxy object, or null if not found. public static GalaxyObject? FindObjectForTag( GalaxyHierarchyCacheEntry entry, string tagAddress) @@ -148,6 +151,7 @@ public static class GalaxyHierarchyProjector /// Finds an attribute in the hierarchy by its tag address. /// The Galaxy hierarchy cache entry. /// The tag address to search for. + /// The matching Galaxy attribute, or null if not found. public static GalaxyAttribute? FindAttributeForTag( GalaxyHierarchyCacheEntry entry, string tagAddress) @@ -165,6 +169,7 @@ public static class GalaxyHierarchyProjector /// Gets the contained path for an object by its gobject ID. /// The Galaxy hierarchy cache entry. /// The Galaxy object ID. + /// The contained path string, or an empty string if the object is not found. public static string GetContainedPath( GalaxyHierarchyCacheEntry entry, int gobjectId) @@ -282,6 +287,7 @@ public static class GalaxyHierarchyProjector /// Computes a stable filter signature for memoization purposes. /// The discovery hierarchy request. /// Optional glob patterns to filter browse subtrees. + /// A string key that uniquely identifies the combination of filter parameters. public static string ComputeFilterSignature( DiscoverHierarchyRequest request, IReadOnlyList? browseSubtreeGlobs) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyRepository.cs b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyRepository.cs index db3c171..367885e 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyRepository.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/GalaxyRepository.cs @@ -15,8 +15,7 @@ namespace ZB.MOM.WW.MxGateway.Server.Galaxy; /// public sealed class GalaxyRepository(GalaxyRepositoryOptions options) : IGalaxyRepository { - /// Tests the connection to the Galaxy Repository database. - /// Token to cancel the asynchronous operation. + /// public async Task TestConnectionAsync(CancellationToken ct = default) { try @@ -31,8 +30,7 @@ public sealed class GalaxyRepository(GalaxyRepositoryOptions options) : IGalaxyR catch (InvalidOperationException) { return false; } } - /// Retrieves the last deployment time from the Galaxy Repository. - /// Token to cancel the asynchronous operation. + /// public async Task GetLastDeployTimeAsync(CancellationToken ct = default) { using SqlConnection conn = new(options.ConnectionString); @@ -43,8 +41,7 @@ public sealed class GalaxyRepository(GalaxyRepositoryOptions options) : IGalaxyR return result is DateTime dt ? dt : null; } - /// Retrieves the complete hierarchy of Galaxy objects from the repository. - /// Token to cancel the asynchronous operation. + /// public async Task> GetHierarchyAsync(CancellationToken ct = default) { List rows = new(); @@ -81,8 +78,7 @@ public sealed class GalaxyRepository(GalaxyRepositoryOptions options) : IGalaxyR return rows; } - /// Retrieves all attributes for Galaxy objects from the repository. - /// Token to cancel the asynchronous operation. + /// public async Task> GetAttributesAsync(CancellationToken ct = default) { List rows = new(); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/IGalaxyHierarchyCache.cs b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/IGalaxyHierarchyCache.cs index 0737276..0428312 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/IGalaxyHierarchyCache.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/IGalaxyHierarchyCache.cs @@ -13,6 +13,7 @@ public interface IGalaxyHierarchyCache /// refresh. /// /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. Task RefreshAsync(CancellationToken cancellationToken); /// @@ -21,5 +22,6 @@ public interface IGalaxyHierarchyCache /// very first request after gateway start. /// /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. Task WaitForFirstLoadAsync(CancellationToken cancellationToken); } diff --git a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/IGalaxyHierarchySnapshotStore.cs b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/IGalaxyHierarchySnapshotStore.cs index 53556d5..93f1c33 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/IGalaxyHierarchySnapshotStore.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/IGalaxyHierarchySnapshotStore.cs @@ -13,6 +13,7 @@ public interface IGalaxyHierarchySnapshotStore /// /// The browse dataset to persist. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous save operation. Task SaveAsync(GalaxyHierarchySnapshot snapshot, CancellationToken cancellationToken); /// diff --git a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/IGalaxyRepository.cs b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/IGalaxyRepository.cs index 6621837..36b173c 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Galaxy/IGalaxyRepository.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Galaxy/IGalaxyRepository.cs @@ -14,17 +14,21 @@ public interface IGalaxyRepository { /// Tests the connection to the Galaxy Repository database. /// Token to cancel the asynchronous operation. + /// A task that resolves to if the connection succeeds; otherwise . Task TestConnectionAsync(CancellationToken ct = default); /// Retrieves the last deployment time from the Galaxy Repository. /// Token to cancel the asynchronous operation. + /// A task that resolves to the last deploy time, or if not available. Task GetLastDeployTimeAsync(CancellationToken ct = default); /// Retrieves the complete hierarchy of Galaxy objects from the repository. /// Token to cancel the asynchronous operation. + /// A task that resolves to the list of hierarchy rows. Task> GetHierarchyAsync(CancellationToken ct = default); /// Retrieves all attributes for Galaxy objects from the repository. /// Token to cancel the asynchronous operation. + /// A task that resolves to the list of attribute rows. Task> GetAttributesAsync(CancellationToken ct = default); } diff --git a/src/ZB.MOM.WW.MxGateway.Server/Grpc/EventStreamService.cs b/src/ZB.MOM.WW.MxGateway.Server/Grpc/EventStreamService.cs index a519f58..2f37f89 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Grpc/EventStreamService.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Grpc/EventStreamService.cs @@ -18,12 +18,7 @@ public sealed class EventStreamService( IDashboardEventBroadcaster dashboardEventBroadcaster, ILogger logger) : IEventStreamService { - /// - /// Streams events from a session to the client asynchronously. - /// - /// Stream events request. - /// Cancellation token. - /// Async enumerable of MX events. + /// public async IAsyncEnumerable StreamEventsAsync( StreamEventsRequest request, [EnumeratorCancellation] CancellationToken cancellationToken) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Grpc/GalaxyProtoMapper.cs b/src/ZB.MOM.WW.MxGateway.Server/Grpc/GalaxyProtoMapper.cs index 5102709..bebd7b3 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Grpc/GalaxyProtoMapper.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Grpc/GalaxyProtoMapper.cs @@ -13,6 +13,7 @@ public static class GalaxyProtoMapper /// Maps Galaxy hierarchy and attribute rows to Galaxy object protos. /// Hierarchy rows from Galaxy Repository. /// Attribute rows from Galaxy Repository. + /// An enumerable of mapped Galaxy object protos. public static IEnumerable MapHierarchy( IReadOnlyList hierarchy, IReadOnlyList attributes) @@ -30,6 +31,7 @@ public static class GalaxyProtoMapper /// Maps a Galaxy hierarchy row to a Galaxy object proto. /// Hierarchy row from Galaxy Repository. /// Attributes indexed by gobject ID. + /// The mapped Galaxy object proto. public static GalaxyObject MapObject( GalaxyHierarchyRow row, IReadOnlyDictionary> attributesByGobjectId) @@ -60,6 +62,7 @@ public static class GalaxyProtoMapper /// Maps a Galaxy attribute row to a Galaxy attribute proto. /// Attribute row from Galaxy Repository. + /// The mapped Galaxy attribute proto. public static GalaxyAttribute MapAttribute(GalaxyAttributeRow row) => new() { AttributeName = row.AttributeName, diff --git a/src/ZB.MOM.WW.MxGateway.Server/Grpc/IEventStreamService.cs b/src/ZB.MOM.WW.MxGateway.Server/Grpc/IEventStreamService.cs index ace7fce..9bc9892 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Grpc/IEventStreamService.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Grpc/IEventStreamService.cs @@ -12,6 +12,7 @@ public interface IEventStreamService /// /// Request payload. /// Token to cancel the asynchronous operation. + /// An async enumerable of MXAccess events. IAsyncEnumerable StreamEventsAsync( StreamEventsRequest request, CancellationToken cancellationToken); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Grpc/MxAccessGatewayService.cs b/src/ZB.MOM.WW.MxGateway.Server/Grpc/MxAccessGatewayService.cs index 362ad46..1d7303a 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Grpc/MxAccessGatewayService.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Grpc/MxAccessGatewayService.cs @@ -162,15 +162,6 @@ public sealed class MxAccessGatewayService( } /// - /// - /// Surfaces the public AcknowledgeAlarm RPC. Acknowledgement is - /// session-less: the gateway routes it through the always-on - /// monitor session. An - /// alarm_full_reference that parses as a canonical GUID forwards - /// to AcknowledgeAlarmCommand; a Provider!Group.Tag - /// reference forwards to AcknowledgeAlarmByNameCommand; anything - /// else returns an InvalidRequest diagnostic in the reply. - /// public override async Task AcknowledgeAlarm( AcknowledgeAlarmRequest request, ServerCallContext context) @@ -193,14 +184,6 @@ public sealed class MxAccessGatewayService( } /// - /// - /// Surfaces the public StreamAlarms RPC — the session-less central - /// alarm feed. The stream opens with one active_alarm per - /// currently-active alarm, then a single snapshot_complete, then - /// a transition for every subsequent change. Served by the - /// gateway's always-on monitor; any - /// number of clients fan out from the single monitor. - /// public override async Task StreamAlarms( StreamAlarmsRequest request, IServerStreamWriter responseStream, @@ -224,12 +207,6 @@ public sealed class MxAccessGatewayService( } /// - /// - /// Snapshot of the active-alarm cache maintained by the gateway's - /// always-on alarm monitor. Streams one - /// per currently-active alarm and completes — no transitions are - /// emitted. Use StreamAlarms for a live transition feed. - /// public override async Task QueryActiveAlarms( QueryActiveAlarmsRequest request, IServerStreamWriter responseStream, diff --git a/src/ZB.MOM.WW.MxGateway.Server/Grpc/MxAccessGrpcMapper.cs b/src/ZB.MOM.WW.MxGateway.Server/Grpc/MxAccessGrpcMapper.cs index 6e7e354..f0070db 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Grpc/MxAccessGrpcMapper.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Grpc/MxAccessGrpcMapper.cs @@ -23,6 +23,7 @@ public sealed class MxAccessGrpcMapper /// Maps a gRPC MX command request to a worker command. /// /// Request payload. + /// The mapped worker command. public WorkerCommand MapCommand(MxCommandRequest request) { ArgumentNullException.ThrowIfNull(request); @@ -39,6 +40,7 @@ public sealed class MxAccessGrpcMapper /// Maps a worker command reply to a gRPC MX command reply. /// /// Worker command reply. + /// The mapped gRPC command reply. public MxCommandReply MapCommandReply(WorkerCommandReply reply) { ArgumentNullException.ThrowIfNull(reply); @@ -58,6 +60,7 @@ public sealed class MxAccessGrpcMapper /// Maps a worker event to a gRPC MX event. /// /// Worker event to map. + /// The mapped gRPC MX event. public MxEvent MapEvent(WorkerEvent workerEvent) { ArgumentNullException.ThrowIfNull(workerEvent); @@ -73,6 +76,7 @@ public sealed class MxAccessGrpcMapper /// Creates an OK protocol status. /// /// Status message. + /// A with code . public static ProtocolStatus Ok(string message = "OK") { return new ProtocolStatus @@ -86,6 +90,7 @@ public sealed class MxAccessGrpcMapper /// Creates an InvalidRequest protocol status. /// /// Status message. + /// A with code . public static ProtocolStatus InvalidRequest(string message) { return new ProtocolStatus @@ -99,6 +104,7 @@ public sealed class MxAccessGrpcMapper /// Creates a SessionNotFound protocol status. /// /// Status message. + /// A with code . public static ProtocolStatus SessionNotFound(string message) { return new ProtocolStatus @@ -112,6 +118,7 @@ public sealed class MxAccessGrpcMapper /// Creates a SessionNotReady protocol status. /// /// Status message. + /// A with code . public static ProtocolStatus SessionNotReady(string message) { return new ProtocolStatus @@ -125,6 +132,7 @@ public sealed class MxAccessGrpcMapper /// Creates a WorkerUnavailable protocol status. /// /// Status message. + /// A with code . public static ProtocolStatus WorkerUnavailable(string message) { return new ProtocolStatus @@ -138,6 +146,7 @@ public sealed class MxAccessGrpcMapper /// Creates a Timeout protocol status. /// /// Status message. + /// A with code . public static ProtocolStatus Timeout(string message) { return new ProtocolStatus @@ -151,6 +160,7 @@ public sealed class MxAccessGrpcMapper /// Creates a Canceled protocol status. /// /// Status message. + /// A with code . public static ProtocolStatus Canceled(string message) { return new ProtocolStatus @@ -164,6 +174,7 @@ public sealed class MxAccessGrpcMapper /// Creates a ProtocolViolation protocol status. /// /// Status message. + /// A with code . public static ProtocolStatus ProtocolViolation(string message) { return new ProtocolStatus diff --git a/src/ZB.MOM.WW.MxGateway.Server/Metrics/GatewayMetrics.cs b/src/ZB.MOM.WW.MxGateway.Server/Metrics/GatewayMetrics.cs index 8727478..9d51983 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Metrics/GatewayMetrics.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Metrics/GatewayMetrics.cs @@ -380,6 +380,7 @@ public sealed class GatewayMetrics : IDisposable /// /// Returns a snapshot of all current metric values. /// + /// A consistent snapshot of the current metric counters and gauges. public GatewayMetricsSnapshot GetSnapshot() { lock (_syncRoot) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Security/Audit/CanonicalAuditWriter.cs b/src/ZB.MOM.WW.MxGateway.Server/Security/Audit/CanonicalAuditWriter.cs index 06a7ad7..b94558f 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Security/Audit/CanonicalAuditWriter.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Security/Audit/CanonicalAuditWriter.cs @@ -19,7 +19,10 @@ public sealed class CanonicalAuditWriter( SqliteCanonicalAuditStore store, ILogger logger) : IAuditWriter { - /// + /// Persists the audit event to the canonical store; swallows and logs any write failure rather than propagating it. + /// The audit event to persist. + /// Token to observe for cancellation. + /// A task that represents the asynchronous operation. public async Task WriteAsync(AuditEvent auditEvent, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(auditEvent); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Security/Audit/CanonicalForwardingApiKeyAuditStore.cs b/src/ZB.MOM.WW.MxGateway.Server/Security/Audit/CanonicalForwardingApiKeyAuditStore.cs index befb60b..e947fb0 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Security/Audit/CanonicalForwardingApiKeyAuditStore.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Security/Audit/CanonicalForwardingApiKeyAuditStore.cs @@ -43,7 +43,10 @@ public sealed class CanonicalForwardingApiKeyAuditStore( /// The library's keyless schema-init event type. private const string InitDbEventType = "init-db"; - /// + /// Converts the library audit entry to a canonical and forwards it through the gateway's audit writer. + /// The API key audit entry to append. + /// Token to observe for cancellation. + /// A task that represents the asynchronous operation. public async Task AppendAsync(ApiKeyAuditEntry entry, CancellationToken ct) { ArgumentNullException.ThrowIfNull(entry); @@ -71,7 +74,10 @@ public sealed class CanonicalForwardingApiKeyAuditStore( await auditWriter.WriteAsync(auditEvent, ct).ConfigureAwait(false); } - /// + /// Reads recent audit events from the canonical store and maps them back to library-compatible records. + /// Maximum number of entries to return. + /// Token to observe for cancellation. + /// A task that resolves to the most recent audit entries, up to items. public async Task> ListRecentAsync(int limit, CancellationToken ct) { IReadOnlyList events = await store.ListRecentAsync(limit, ct).ConfigureAwait(false); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Security/Audit/SqliteCanonicalAuditStore.cs b/src/ZB.MOM.WW.MxGateway.Server/Security/Audit/SqliteCanonicalAuditStore.cs index 64c1133..a617982 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Security/Audit/SqliteCanonicalAuditStore.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Security/Audit/SqliteCanonicalAuditStore.cs @@ -43,6 +43,7 @@ public sealed class SqliteCanonicalAuditStore(AuthSqliteConnectionFactory connec /// Inserts a canonical audit event into the audit_event table. /// The canonical event to persist. /// Token to observe for cancellation. + /// A task that represents the asynchronous operation. public async Task InsertAsync(AuditEvent auditEvent, CancellationToken cancellationToken) { ArgumentNullException.ThrowIfNull(auditEvent); @@ -79,6 +80,7 @@ public sealed class SqliteCanonicalAuditStore(AuthSqliteConnectionFactory connec /// Returns the most recent canonical audit events, newest first. /// Maximum number of events to return. /// Token to observe for cancellation. + /// A task that resolves to the most recent audit events, up to . public async Task> ListRecentAsync(int limit, CancellationToken cancellationToken) { if (limit <= 0) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Security/Authentication/ApiKeyAdminCliRunner.cs b/src/ZB.MOM.WW.MxGateway.Server/Security/Authentication/ApiKeyAdminCliRunner.cs index 24d01e6..63d4fe2 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Security/Authentication/ApiKeyAdminCliRunner.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Security/Authentication/ApiKeyAdminCliRunner.cs @@ -26,6 +26,7 @@ public sealed class ApiKeyAdminCliRunner(ApiKeyAdminCommands commands) /// API key administration command to execute. /// Text writer for command output. /// Token to cancel the asynchronous operation. + /// A task that resolves to the exit code (always 0 on success). public async Task RunAsync( ApiKeyAdminCommand command, TextWriter output, diff --git a/src/ZB.MOM.WW.MxGateway.Server/Security/Authentication/ApiKeyAdminParseResult.cs b/src/ZB.MOM.WW.MxGateway.Server/Security/Authentication/ApiKeyAdminParseResult.cs index 412e7b4..a221e21 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Security/Authentication/ApiKeyAdminParseResult.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Security/Authentication/ApiKeyAdminParseResult.cs @@ -6,6 +6,7 @@ public sealed record ApiKeyAdminParseResult( string? Error) { /// Returns a result indicating the input was not an API key command. + /// A parse result with set to false. public static ApiKeyAdminParseResult NotApiKeyCommand() { return new ApiKeyAdminParseResult(false, null, null); @@ -13,6 +14,7 @@ public sealed record ApiKeyAdminParseResult( /// Returns a successful parse result with the parsed API key command. /// Parsed API key administration command. + /// A parse result with set to true and the command populated. public static ApiKeyAdminParseResult Success(ApiKeyAdminCommand command) { return new ApiKeyAdminParseResult(true, command, null); @@ -20,6 +22,7 @@ public sealed record ApiKeyAdminParseResult( /// Returns a parse result with the specified error message. /// Error message describing the parse failure. + /// A parse result with set to true and the error message populated. public static ApiKeyAdminParseResult Fail(string error) { return new ApiKeyAdminParseResult(true, null, error); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Security/Authentication/ApiKeyConstraintSerializer.cs b/src/ZB.MOM.WW.MxGateway.Server/Security/Authentication/ApiKeyConstraintSerializer.cs index d2125ba..43ca9d6 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Security/Authentication/ApiKeyConstraintSerializer.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Security/Authentication/ApiKeyConstraintSerializer.cs @@ -12,6 +12,7 @@ public static class ApiKeyConstraintSerializer /// Serializes API key constraints to JSON, or returns null if the constraints are empty. /// The constraints to serialize. + /// A JSON string representing the constraints, or if empty. public static string? Serialize(ApiKeyConstraints constraints) { ArgumentNullException.ThrowIfNull(constraints); @@ -20,6 +21,7 @@ public static class ApiKeyConstraintSerializer /// Deserializes API key constraints from JSON, or returns empty constraints if JSON is null or whitespace. /// The JSON string to deserialize. + /// The deserialized , or when is null or whitespace. public static ApiKeyConstraints Deserialize(string? json) { if (string.IsNullOrWhiteSpace(json)) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/ConstraintEnforcer.cs b/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/ConstraintEnforcer.cs index 5865a5d..c048068 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/ConstraintEnforcer.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/ConstraintEnforcer.cs @@ -16,10 +16,7 @@ public sealed class ConstraintEnforcer( IGalaxyHierarchyCache cache, IAuditWriter auditWriter) : IConstraintEnforcer { - /// Checks read constraints on a tag address. - /// The API key identity to check constraints for. - /// Tag address to validate. - /// Token to observe for cancellation. + /// public Task CheckReadTagAsync( ApiKeyIdentity? identity, string tagAddress, @@ -34,12 +31,7 @@ public sealed class ConstraintEnforcer( return Task.FromResult(CheckReadTarget(constraints, tagAddress)); } - /// Checks read constraints on a server and item handle. - /// The API key identity to check constraints for. - /// The gateway session containing handle registrations. - /// The MXAccess server handle. - /// The MXAccess item handle. - /// Token to observe for cancellation. + /// public Task CheckReadHandleAsync( ApiKeyIdentity? identity, GatewaySession session, @@ -61,12 +53,7 @@ public sealed class ConstraintEnforcer( return Task.FromResult(CheckReadTarget(constraints, registration.TagAddress)); } - /// Checks write constraints on a server and item handle. - /// The API key identity to check constraints for. - /// The gateway session containing handle registrations. - /// The MXAccess server handle. - /// The MXAccess item handle. - /// Token to observe for cancellation. + /// public Task CheckWriteHandleAsync( ApiKeyIdentity? identity, GatewaySession session, @@ -115,12 +102,7 @@ public sealed class ConstraintEnforcer( return Task.FromResult(null); } - /// Records a constraint denial audit entry. - /// The API key identity that was denied. - /// The command type (e.g., read, write). - /// The target being accessed (tag address or handle). - /// The constraint failure details. - /// Token to observe for cancellation. + /// public async Task RecordDenialAsync( ApiKeyIdentity? identity, string commandKind, diff --git a/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/GatewayRequestIdentityAccessor.cs b/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/GatewayRequestIdentityAccessor.cs index 8727deb..3c64516 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/GatewayRequestIdentityAccessor.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/GatewayRequestIdentityAccessor.cs @@ -6,12 +6,10 @@ public sealed class GatewayRequestIdentityAccessor : IGatewayRequestIdentityAcce { private readonly AsyncLocal currentIdentity = new(); - /// Gets the current request identity. + /// public ApiKeyIdentity? Current => currentIdentity.Value; - /// Sets the current identity and returns a scope that restores the previous identity. - /// The identity to push. - /// Disposable scope. + /// public IDisposable Push(ApiKeyIdentity identity) { ArgumentNullException.ThrowIfNull(identity); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/GrpcAuthorizationServiceCollectionExtensions.cs b/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/GrpcAuthorizationServiceCollectionExtensions.cs index f174fc0..2e3c044 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/GrpcAuthorizationServiceCollectionExtensions.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/GrpcAuthorizationServiceCollectionExtensions.cs @@ -13,6 +13,7 @@ public static class GrpcAuthorizationServiceCollectionExtensions /// Registers gRPC authorization middleware and scope resolver. /// /// Service collection to register dependencies into. + /// The same for fluent chaining. public static IServiceCollection AddGatewayGrpcAuthorization(this IServiceCollection services) { services.AddSingleton(); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/IConstraintEnforcer.cs b/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/IConstraintEnforcer.cs index 91d6891..4fae6f8 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/IConstraintEnforcer.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/IConstraintEnforcer.cs @@ -9,6 +9,7 @@ public interface IConstraintEnforcer /// The API key identity. /// Tag address to check. /// Token to observe for cancellation. + /// A task that resolves to the constraint failure, or null if the check passes. Task CheckReadTagAsync( ApiKeyIdentity? identity, string tagAddress, @@ -20,6 +21,7 @@ public interface IConstraintEnforcer /// The MXAccess server handle. /// The MXAccess item handle. /// Token to observe for cancellation. + /// A task that resolves to the constraint failure, or null if the check passes. Task CheckReadHandleAsync( ApiKeyIdentity? identity, GatewaySession session, @@ -33,6 +35,7 @@ public interface IConstraintEnforcer /// The MXAccess server handle. /// The MXAccess item handle. /// Token to observe for cancellation. + /// A task that resolves to the constraint failure, or null if the check passes. Task CheckWriteHandleAsync( ApiKeyIdentity? identity, GatewaySession session, @@ -46,6 +49,7 @@ public interface IConstraintEnforcer /// The target of the denied command. /// The constraint failure details. /// Token to observe for cancellation. + /// A task that represents the asynchronous operation. Task RecordDenialAsync( ApiKeyIdentity? identity, string commandKind, diff --git a/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/IGatewayRequestIdentityAccessor.cs b/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/IGatewayRequestIdentityAccessor.cs index c6fe5f0..e38fcef 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/IGatewayRequestIdentityAccessor.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Security/Authorization/IGatewayRequestIdentityAccessor.cs @@ -10,5 +10,6 @@ public interface IGatewayRequestIdentityAccessor /// Temporarily pushes an identity onto the scope stack, returning a handle to restore the previous state. /// API key identity to push. + /// A disposable scope that restores the previous identity when disposed. IDisposable Push(ApiKeyIdentity identity); } diff --git a/src/ZB.MOM.WW.MxGateway.Server/Security/Tls/KestrelTlsInspector.cs b/src/ZB.MOM.WW.MxGateway.Server/Security/Tls/KestrelTlsInspector.cs index 3c80573..770e7e3 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Security/Tls/KestrelTlsInspector.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Security/Tls/KestrelTlsInspector.cs @@ -16,6 +16,8 @@ public static class KestrelTlsInspector /// Certificate:Thumbprint), meaning the gateway must supply a /// generated fallback certificate. /// + /// Application configuration containing the Kestrel endpoint settings. + /// if a generated certificate is required; otherwise . public static bool RequiresGeneratedCertificate(IConfiguration configuration) { // A Kestrel default certificate applies to every endpoint that lacks its own. diff --git a/src/ZB.MOM.WW.MxGateway.Server/Security/Tls/SelfSignedCertificateProvider.cs b/src/ZB.MOM.WW.MxGateway.Server/Security/Tls/SelfSignedCertificateProvider.cs index 17b33cb..7ed10bc 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Security/Tls/SelfSignedCertificateProvider.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Security/Tls/SelfSignedCertificateProvider.cs @@ -20,6 +20,10 @@ public sealed class SelfSignedCertificateProvider private readonly ILogger _logger; private readonly TimeProvider _timeProvider; + /// Initializes a new instance of the class. + /// TLS configuration options controlling certificate subject, validity, and storage. + /// Logger for certificate lifecycle events. + /// Time provider used to compute certificate validity windows. public SelfSignedCertificateProvider( TlsOptions options, ILogger logger, @@ -31,6 +35,7 @@ public sealed class SelfSignedCertificateProvider } /// Creates a fresh in-memory ECDSA P-256 self-signed certificate. + /// A new self-signed with the configured SANs and validity period. public X509Certificate2 GenerateCertificate() { using ECDsa key = ECDsa.Create(ECCurve.NamedCurves.nistP256); @@ -89,6 +94,7 @@ public sealed class SelfSignedCertificateProvider /// Loads the persisted certificate, regenerating when missing, /// expired (and allowed), or unreadable. + /// The loaded or newly generated . public X509Certificate2 LoadOrCreate() { string path = _options.SelfSignedCertPath; diff --git a/src/ZB.MOM.WW.MxGateway.Server/Sessions/GatewaySession.cs b/src/ZB.MOM.WW.MxGateway.Server/Sessions/GatewaySession.cs index eebfb4e..99916da 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Sessions/GatewaySession.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Sessions/GatewaySession.cs @@ -370,6 +370,7 @@ public sealed class GatewaySession /// Determines whether the session lease has expired. /// /// Current timestamp for comparison. + /// true if the lease has expired; otherwise false. public bool IsLeaseExpired(DateTimeOffset now) { lock (_syncRoot) @@ -384,6 +385,7 @@ public sealed class GatewaySession /// Attaches an event subscriber and returns a disposable lease. /// /// If true, allows multiple concurrent event subscribers. + /// A disposable that releases the subscriber slot when disposed. public IDisposable AttachEventSubscriber(bool allowMultipleSubscribers) { lock (_syncRoot) @@ -412,6 +414,7 @@ public sealed class GatewaySession /// /// Worker command to invoke. /// Token to cancel the asynchronous operation. + /// A task that resolves to the worker command reply. public async Task InvokeAsync( WorkerCommand command, CancellationToken cancellationToken) @@ -426,6 +429,7 @@ public sealed class GatewaySession /// The MXAccess server handle. /// The MXAccess item handle. /// The item registration if found. + /// true if the item registration was found; otherwise false. public bool TryGetItemRegistration( int serverHandle, int itemHandle, @@ -487,6 +491,7 @@ public sealed class GatewaySession /// Server handle returned by the worker. /// Tag addresses to add. /// Token to cancel the asynchronous operation. + /// A task that resolves to the per-item subscribe results. public Task> AddItemBulkAsync( int serverHandle, IReadOnlyList tagAddresses, @@ -512,6 +517,7 @@ public sealed class GatewaySession /// Server handle returned by the worker. /// Item handles to advise. /// Token to cancel the asynchronous operation. + /// A task that resolves to the per-item subscribe results. public Task> AdviseItemBulkAsync( int serverHandle, IReadOnlyList itemHandles, @@ -537,6 +543,7 @@ public sealed class GatewaySession /// Server handle returned by the worker. /// Item handles to remove. /// Token to cancel the asynchronous operation. + /// A task that resolves to the per-item subscribe results. public Task> RemoveItemBulkAsync( int serverHandle, IReadOnlyList itemHandles, @@ -562,6 +569,7 @@ public sealed class GatewaySession /// Server handle returned by the worker. /// Item handles to un-advise. /// Token to cancel the asynchronous operation. + /// A task that resolves to the per-item subscribe results. public Task> UnAdviseItemBulkAsync( int serverHandle, IReadOnlyList itemHandles, @@ -587,6 +595,7 @@ public sealed class GatewaySession /// Server handle returned by the worker. /// Tag addresses to subscribe to. /// Token to cancel the asynchronous operation. + /// A task that resolves to the per-item subscribe results. public Task> SubscribeBulkAsync( int serverHandle, IReadOnlyList tagAddresses, @@ -612,6 +621,7 @@ public sealed class GatewaySession /// Server handle returned by the worker. /// Item handles to unsubscribe from. /// Token to cancel the asynchronous operation. + /// A task that resolves to the per-item subscribe results. public Task> UnsubscribeBulkAsync( int serverHandle, IReadOnlyList itemHandles, @@ -635,6 +645,7 @@ public sealed class GatewaySession /// Server handle returned by the worker. /// Write entries to execute. /// Token to cancel the asynchronous operation. + /// A task that resolves to the per-item write results. public Task> WriteBulkAsync( int serverHandle, IReadOnlyList entries, @@ -658,6 +669,7 @@ public sealed class GatewaySession /// Server handle returned by the worker. /// Write entries to execute. /// Token to cancel the asynchronous operation. + /// A task that resolves to the per-item write results. public Task> Write2BulkAsync( int serverHandle, IReadOnlyList entries, @@ -681,6 +693,7 @@ public sealed class GatewaySession /// Server handle returned by the worker. /// Write entries to execute. /// Token to cancel the asynchronous operation. + /// A task that resolves to the per-item write results. public Task> WriteSecuredBulkAsync( int serverHandle, IReadOnlyList entries, @@ -704,6 +717,7 @@ public sealed class GatewaySession /// Server handle returned by the worker. /// Write entries to execute. /// Token to cancel the asynchronous operation. + /// A task that resolves to the per-item write results. public Task> WriteSecured2BulkAsync( int serverHandle, IReadOnlyList entries, @@ -731,6 +745,7 @@ public sealed class GatewaySession /// Tag addresses to read. /// Timeout for the read operation. /// Token to cancel the asynchronous operation. + /// A task that resolves to the per-item read results. public Task> ReadBulkAsync( int serverHandle, IReadOnlyList tagAddresses, @@ -759,6 +774,7 @@ public sealed class GatewaySession /// Reads events from the worker as an asynchronous enumerable stream. /// /// Token to cancel the asynchronous operation. + /// An async enumerable of worker events. public IAsyncEnumerable ReadEventsAsync(CancellationToken cancellationToken) { IWorkerClient workerClient = GetReadyWorkerClient(); @@ -772,6 +788,7 @@ public sealed class GatewaySession /// /// Reason for closing the session. /// Token to cancel the asynchronous operation. + /// A task that resolves to the session close result. /// /// Concurrent close attempts are serialized by _closeLock so only one close /// runs at a time, but every read/write of _state still passes through @@ -919,6 +936,7 @@ public sealed class GatewaySession /// /// Disposes the session and frees associated resources. /// + /// A task that represents the asynchronous operation. /// /// Acquires _closeLock once before disposing so an in-flight /// finishes before the semaphore is released and diff --git a/src/ZB.MOM.WW.MxGateway.Server/Sessions/ISessionManager.cs b/src/ZB.MOM.WW.MxGateway.Server/Sessions/ISessionManager.cs index 2d84f26..87fff00 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Sessions/ISessionManager.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Sessions/ISessionManager.cs @@ -72,5 +72,6 @@ public interface ISessionManager /// Shuts down all sessions and the session manager. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. Task ShutdownAsync(CancellationToken cancellationToken); } diff --git a/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionManager.cs b/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionManager.cs index e7a07bf..f140102 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionManager.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionManager.cs @@ -53,13 +53,7 @@ public sealed class SessionManager : ISessionManager _sessionSlots = new SemaphoreSlim(_options.Sessions.MaxSessions, _options.Sessions.MaxSessions); } - /// - /// Opens a new gateway session and connects to the worker. - /// - /// Session open request. - /// Client authentication identity. - /// Cancellation token. - /// Opened gateway session. + /// public async Task OpenSessionAsync( SessionOpenRequest request, string? clientIdentity, @@ -123,12 +117,7 @@ public sealed class SessionManager : ISessionManager } } - /// - /// Attempts to retrieve a session by ID. - /// - /// Session identifier. - /// The session if found. - /// True if session found; otherwise false. + /// public bool TryGetSession( string sessionId, [MaybeNullWhen(false)] out GatewaySession session) @@ -136,13 +125,7 @@ public sealed class SessionManager : ISessionManager return _registry.TryGet(sessionId, out session); } - /// - /// Invokes a worker command on a session asynchronously. - /// - /// Session identifier. - /// Worker command. - /// Cancellation token. - /// Command reply. + /// public async Task InvokeAsync( string sessionId, WorkerCommand command, @@ -169,12 +152,7 @@ public sealed class SessionManager : ISessionManager } } - /// - /// Reads events from a session's event stream asynchronously. - /// - /// Session identifier. - /// Cancellation token. - /// Async enumerable of worker events. + /// public IAsyncEnumerable ReadEventsAsync( string sessionId, CancellationToken cancellationToken) @@ -184,12 +162,7 @@ public sealed class SessionManager : ISessionManager return session.ReadEventsAsync(cancellationToken); } - /// - /// Closes a gateway session asynchronously. - /// - /// Session identifier. - /// Cancellation token. - /// Session close result. + /// public async Task CloseSessionAsync( string sessionId, CancellationToken cancellationToken) @@ -203,16 +176,7 @@ public sealed class SessionManager : ISessionManager return result; } - /// - /// Forcefully terminates a session's worker without attempting graceful shutdown. - /// Mirrors the registry/metrics cleanup that - /// performs after a successful close, but skips the WorkerClient.ShutdownAsync - /// step that would otherwise attempt. - /// - /// Session identifier. - /// Reason recorded for the kill. - /// Cancellation token. - /// Session close result. + /// public async Task KillWorkerAsync( string sessionId, string reason, @@ -263,12 +227,7 @@ public sealed class SessionManager : ISessionManager return new SessionCloseResult(sessionId, SessionState.Closed, AlreadyClosed: wasClosed); } - /// - /// Closes all sessions with expired leases asynchronously. - /// - /// Current time for lease expiration check. - /// Cancellation token. - /// Count of sessions closed. + /// public async Task CloseExpiredLeasesAsync( DateTimeOffset now, CancellationToken cancellationToken) @@ -288,11 +247,7 @@ public sealed class SessionManager : ISessionManager return closedCount; } - /// - /// Shuts down all active sessions gracefully asynchronously. - /// - /// Cancellation token. - /// Completed task. + /// public async Task ShutdownAsync(CancellationToken cancellationToken) { foreach (GatewaySession session in _registry.Snapshot()) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionOpenRequest.cs b/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionOpenRequest.cs index c48eacf..d60ffe8 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionOpenRequest.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionOpenRequest.cs @@ -11,6 +11,7 @@ public sealed record SessionOpenRequest( { /// Creates a SessionOpenRequest from a gRPC OpenSessionRequest contract. /// Request payload. + /// A new populated from the contract fields. public static SessionOpenRequest FromContract(OpenSessionRequest request) { ArgumentNullException.ThrowIfNull(request); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionRegistry.cs b/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionRegistry.cs index ce145cc..e687a06 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionRegistry.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionRegistry.cs @@ -11,20 +11,13 @@ public sealed class SessionRegistry : ISessionRegistry { private readonly ConcurrentDictionary _sessions = new(StringComparer.Ordinal); - /// - /// Gets the total count of sessions in the registry. - /// + /// public int Count => _sessions.Count; - /// - /// Gets the count of non-closed sessions. - /// + /// public int ActiveCount => _sessions.Values.Count(session => session.State is not SessionState.Closed); - /// - /// Adds a session to the registry. - /// - /// Gateway session to add. + /// public bool TryAdd(GatewaySession session) { ArgumentNullException.ThrowIfNull(session); @@ -32,11 +25,7 @@ public sealed class SessionRegistry : ISessionRegistry return _sessions.TryAdd(session.SessionId, session); } - /// - /// Retrieves a session by identifier. - /// - /// Identifier of the session. - /// The retrieved session if found. + /// public bool TryGet( string sessionId, [MaybeNullWhen(false)] out GatewaySession session) @@ -44,11 +33,7 @@ public sealed class SessionRegistry : ISessionRegistry return _sessions.TryGetValue(sessionId, out session); } - /// - /// Removes a session from the registry by identifier. - /// - /// Identifier of the session. - /// The removed session if found. + /// public bool TryRemove( string sessionId, [MaybeNullWhen(false)] out GatewaySession session) @@ -56,9 +41,7 @@ public sealed class SessionRegistry : ISessionRegistry return _sessions.TryRemove(sessionId, out session); } - /// - /// Returns a snapshot of all sessions in the registry. - /// + /// public IReadOnlyCollection Snapshot() { return _sessions.Values.ToArray(); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionShutdownHostedService.cs b/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionShutdownHostedService.cs index 426fa29..f403121 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionShutdownHostedService.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionShutdownHostedService.cs @@ -8,13 +8,17 @@ public sealed class SessionShutdownHostedService( ISessionManager sessionManager, ILogger logger) : IHostedService { - /// + /// No-op start handler; this service only acts on shutdown. + /// Cancellation token (unused). + /// A completed task. public Task StartAsync(CancellationToken cancellationToken) { return Task.CompletedTask; } - /// + /// Shuts down all active gateway sessions gracefully. + /// Token signaled when the host shutdown deadline is reached. + /// A task that represents the asynchronous shutdown operation. public async Task StopAsync(CancellationToken cancellationToken) { try diff --git a/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionWorkerClientFactory.cs b/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionWorkerClientFactory.cs index eb95139..4a14f6b 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionWorkerClientFactory.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Sessions/SessionWorkerClientFactory.cs @@ -39,10 +39,7 @@ public sealed class SessionWorkerClientFactory : ISessionWorkerClientFactory _options = options.Value; } - /// Creates a worker client and launches the worker process. - /// The gateway session. - /// Cancellation token. - /// The created worker client. + /// public async Task CreateAsync( GatewaySession session, CancellationToken cancellationToken) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Workers/IWorkerClient.cs b/src/ZB.MOM.WW.MxGateway.Server/Workers/IWorkerClient.cs index abad654..33d8fdd 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Workers/IWorkerClient.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Workers/IWorkerClient.cs @@ -19,12 +19,14 @@ public interface IWorkerClient : IAsyncDisposable /// Initiates the handshake and enters ready state. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. Task StartAsync(CancellationToken cancellationToken); /// Sends a command to the worker and waits for a reply. /// Worker command to invoke. /// Timeout for waiting for the reply. /// Token to cancel the asynchronous operation. + /// A task that resolves to the worker command reply. Task InvokeAsync( WorkerCommand command, TimeSpan timeout, @@ -32,11 +34,13 @@ public interface IWorkerClient : IAsyncDisposable /// Reads events from the worker as they arrive. /// Token to cancel the asynchronous operation. + /// An async sequence of worker events. IAsyncEnumerable ReadEventsAsync(CancellationToken cancellationToken); /// Gracefully shuts down the worker by closing the connection. /// Timeout for shutdown. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. Task ShutdownAsync(TimeSpan timeout, CancellationToken cancellationToken); /// Terminates the worker process immediately with a diagnostic reason. diff --git a/src/ZB.MOM.WW.MxGateway.Server/Workers/IWorkerProcess.cs b/src/ZB.MOM.WW.MxGateway.Server/Workers/IWorkerProcess.cs index 0249509..b4e85a2 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Workers/IWorkerProcess.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Workers/IWorkerProcess.cs @@ -24,6 +24,7 @@ public interface IWorkerProcess : IDisposable /// Waits for the process to exit with the specified cancellation token. /// /// Token to cancel the asynchronous operation. + /// A value task that completes when the process exits. ValueTask WaitForExitAsync(CancellationToken cancellationToken); /// diff --git a/src/ZB.MOM.WW.MxGateway.Server/Workers/OrphanWorkerCleanupHostedService.cs b/src/ZB.MOM.WW.MxGateway.Server/Workers/OrphanWorkerCleanupHostedService.cs index efa4a49..5d05c6d 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Workers/OrphanWorkerCleanupHostedService.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Workers/OrphanWorkerCleanupHostedService.cs @@ -8,7 +8,9 @@ public sealed class OrphanWorkerCleanupHostedService( OrphanWorkerTerminator terminator, ILogger logger) : IHostedService { - /// + /// Terminates any orphaned MXAccess worker processes found at startup. + /// Token to observe for cancellation (unused; cleanup is best-effort). + /// A completed task. public Task StartAsync(CancellationToken cancellationToken) { try @@ -25,6 +27,8 @@ public sealed class OrphanWorkerCleanupHostedService( return Task.CompletedTask; } - /// + /// Performs no action on stop; all cleanup runs at startup. + /// Token to observe for cancellation (unused). + /// A completed task. public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } diff --git a/src/ZB.MOM.WW.MxGateway.Server/Workers/SystemWorkerProcess.cs b/src/ZB.MOM.WW.MxGateway.Server/Workers/SystemWorkerProcess.cs index ac86232..698ef12 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Workers/SystemWorkerProcess.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Workers/SystemWorkerProcess.cs @@ -28,7 +28,7 @@ internal sealed class SystemWorkerProcess(Process process) : IWorkerProcess process.Kill(entireProcessTree); } - /// + /// Releases the underlying process resources. public void Dispose() { process.Dispose(); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerClient.cs b/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerClient.cs index 3761ddb..54ab22f 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerClient.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerClient.cs @@ -78,10 +78,10 @@ public sealed class WorkerClient : IWorkerClient _lastHeartbeatAt = _timeProvider.GetUtcNow(); } - /// Gets the worker's session ID. + /// public string SessionId => _connection.SessionId; - /// Gets the worker process ID. + /// public int? ProcessId { get @@ -93,7 +93,7 @@ public sealed class WorkerClient : IWorkerClient } } - /// Gets the current client state. + /// public WorkerClientState State { get @@ -105,7 +105,7 @@ public sealed class WorkerClient : IWorkerClient } } - /// Gets the timestamp of the last received heartbeat. + /// public DateTimeOffset LastHeartbeatAt { get @@ -117,8 +117,7 @@ public sealed class WorkerClient : IWorkerClient } } - /// Starts the worker client and completes the handshake. - /// Cancellation token. + /// public async Task StartAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); @@ -141,11 +140,7 @@ public sealed class WorkerClient : IWorkerClient _heartbeatLoopTask = Task.Run(HeartbeatLoopAsync); } - /// Invokes a command on the worker and waits for reply. - /// The command to invoke. - /// Command timeout. - /// Cancellation token. - /// The command reply. + /// public async Task InvokeAsync( WorkerCommand command, TimeSpan timeout, @@ -228,9 +223,7 @@ public sealed class WorkerClient : IWorkerClient } } - /// Reads events from the worker as an async stream. - /// Cancellation token. - /// Async enumerable of worker events. + /// public async IAsyncEnumerable ReadEventsAsync( [EnumeratorCancellation] CancellationToken cancellationToken) { @@ -242,9 +235,7 @@ public sealed class WorkerClient : IWorkerClient } } - /// Shuts down the worker gracefully. - /// Shutdown timeout. - /// Cancellation token. + /// public async Task ShutdownAsync(TimeSpan timeout, CancellationToken cancellationToken) { ThrowIfDisposed(); @@ -289,8 +280,7 @@ public sealed class WorkerClient : IWorkerClient } } - /// Terminates the worker process immediately. - /// Reason for termination. + /// public void Kill(string reason) { ThrowIfDisposed(); @@ -302,6 +292,7 @@ public sealed class WorkerClient : IWorkerClient } /// Disposes the worker client and releases resources. + /// A task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { if (_disposed) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerFrameWriter.cs b/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerFrameWriter.cs index 999fab5..0e8bfe1 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerFrameWriter.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerFrameWriter.cs @@ -30,6 +30,7 @@ public sealed class WorkerFrameWriter /// /// Worker envelope message to write. /// Token to cancel the asynchronous operation. + /// A value task that represents the asynchronous operation. public async ValueTask WriteAsync( WorkerEnvelope envelope, CancellationToken cancellationToken = default) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessHandle.cs b/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessHandle.cs index 0ffb814..619bfd8 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessHandle.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessHandle.cs @@ -30,7 +30,7 @@ public sealed class WorkerProcessHandle : IDisposable /// Gets the time when the process was launched. public DateTimeOffset LaunchedAt { get; } - /// + /// Releases the underlying worker process resources. public void Dispose() { Process.Dispose(); diff --git a/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessLauncher.cs b/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessLauncher.cs index c8a6ab7..cab6fe1 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessLauncher.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessLauncher.cs @@ -58,12 +58,7 @@ public sealed class WorkerProcessLauncher : IWorkerProcessLauncher _logger = logger ?? NullLogger.Instance; } - /// - /// Launches a worker process and waits for startup. - /// - /// Request payload. - /// Token to cancel the asynchronous operation. - /// Handle to the launched worker process. + /// public async Task LaunchAsync( WorkerProcessLaunchRequest request, CancellationToken cancellationToken = default) diff --git a/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessStartedProbe.cs b/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessStartedProbe.cs index 1e19a98..b9caedd 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessStartedProbe.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerProcessStartedProbe.cs @@ -2,11 +2,7 @@ namespace ZB.MOM.WW.MxGateway.Server.Workers; public sealed class WorkerProcessStartedProbe : IWorkerStartupProbe { - /// Verifies that the worker process has started and has not exited. - /// Worker process to verify. - /// Process launch request. - /// Token to cancel the asynchronous operation. - /// Completed task if process is running. + /// public Task WaitUntilReadyAsync( IWorkerProcess process, WorkerProcessLaunchRequest request, diff --git a/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerServiceCollectionExtensions.cs b/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerServiceCollectionExtensions.cs index d15e914..7beb761 100644 --- a/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerServiceCollectionExtensions.cs +++ b/src/ZB.MOM.WW.MxGateway.Server/Workers/WorkerServiceCollectionExtensions.cs @@ -5,6 +5,7 @@ public static class WorkerServiceCollectionExtensions { /// Registers worker process launcher and factory services. /// Service collection to register services. + /// The same to allow chaining. public static IServiceCollection AddWorkerProcessLauncher(this IServiceCollection services) { services.AddSingleton(); diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Configuration/GatewayOptionsValidatorTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Configuration/GatewayOptionsValidatorTests.cs index 49d8165..dff83bb 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Configuration/GatewayOptionsValidatorTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Configuration/GatewayOptionsValidatorTests.cs @@ -46,6 +46,7 @@ public sealed class GatewayOptionsValidatorTests Tls = tls, }; + /// Verifies that the validator succeeds when TLS options are left at their defaults. [Fact] public void Validate_Succeeds_WithDefaultTlsOptions() { @@ -53,6 +54,7 @@ public sealed class GatewayOptionsValidatorTests Assert.True(result.Succeeded); } + /// Verifies that the validator fails when TLS validity years is set to zero. [Fact] public void Validate_Fails_WhenTlsValidityYearsOutOfRange() { @@ -62,6 +64,7 @@ public sealed class GatewayOptionsValidatorTests Assert.Contains(result.Failures!, f => f.Contains("MxGateway:Tls:ValidityYears")); } + /// Verifies that the validator fails when TLS validity years exceeds the allowed maximum. [Fact] public void Validate_Fails_WhenTlsValidityYearsTooLarge() { @@ -71,6 +74,7 @@ public sealed class GatewayOptionsValidatorTests Assert.Contains(result.Failures!, f => f.Contains("MxGateway:Tls:ValidityYears")); } + /// Verifies that the validator fails when an additional DNS name is blank or whitespace. [Fact] public void Validate_Fails_WhenAdditionalDnsNameBlank() { @@ -80,6 +84,7 @@ public sealed class GatewayOptionsValidatorTests Assert.Contains(result.Failures!, f => f.Contains("MxGateway:Tls:AdditionalDnsNames")); } + /// Verifies that the validator fails when the self-signed certificate path is blank. [Fact] public void Validate_Fails_WhenSelfSignedCertPathBlank() { @@ -89,6 +94,7 @@ public sealed class GatewayOptionsValidatorTests Assert.Contains(result.Failures!, f => f.Contains("MxGateway:Tls:SelfSignedCertPath must not be blank.")); } + /// Verifies that the validator fails when LDAP is enabled with port zero. [Fact] public void Validate_Fails_WhenLdapPortIsZero() { @@ -100,6 +106,7 @@ public sealed class GatewayOptionsValidatorTests f => f.Contains("MxGateway:Ldap:Port must be between 1 and 65535 (was 0)")); } + /// Verifies that the validator fails when LDAP is enabled with a port number above 65535. [Fact] public void Validate_Fails_WhenLdapPortExceedsMaximum() { @@ -111,6 +118,7 @@ public sealed class GatewayOptionsValidatorTests f => f.Contains("MxGateway:Ldap:Port must be between 1 and 65535 (was 70000)")); } + /// Verifies that the validator succeeds when LDAP is enabled with a valid port in range. [Fact] public void Validate_Succeeds_WhenLdapEnabledWithValidPort() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Configuration/TlsOptionsBindingTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Configuration/TlsOptionsBindingTests.cs index 796a4ca..599a8ec 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Configuration/TlsOptionsBindingTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Configuration/TlsOptionsBindingTests.cs @@ -6,6 +6,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Configuration; public sealed class TlsOptionsBindingTests { + /// Verifies that TLS option defaults are applied when the configuration section is absent. [Fact] public void Defaults_AreApplied_WhenSectionAbsent() { @@ -16,6 +17,7 @@ public sealed class TlsOptionsBindingTests Assert.False(string.IsNullOrWhiteSpace(options.SelfSignedCertPath)); } + /// Verifies that TLS options bind correctly from the MxGateway:Tls configuration section. [Fact] public void Binds_FromMxGatewayTlsSection() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Dashboard/DashboardBrowseServiceTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Dashboard/DashboardBrowseServiceTests.cs index d749e08..96962c3 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Dashboard/DashboardBrowseServiceTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Dashboard/DashboardBrowseServiceTests.cs @@ -144,7 +144,7 @@ public sealed class DashboardBrowseServiceTests private sealed class StubGalaxyHierarchyCache(GalaxyHierarchyCacheEntry initial) : IGalaxyHierarchyCache { - /// Mutable so a single test can swap the entry mid-flight. + /// public GalaxyHierarchyCacheEntry Current { get; set; } = initial; /// diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Diagnostics/AuthStoreHealthCheckTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Diagnostics/AuthStoreHealthCheckTests.cs index df87ed3..377c67d 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Diagnostics/AuthStoreHealthCheckTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Diagnostics/AuthStoreHealthCheckTests.cs @@ -12,6 +12,8 @@ public sealed class AuthStoreHealthCheckTests return new AuthSqliteConnectionFactory(sqlitePath); } + /// Verifies that the health check reports Healthy when the auth store is reachable. + /// A task that represents the asynchronous operation. [Fact] public async Task Healthy_WhenStoreReachable() { @@ -25,6 +27,8 @@ public sealed class AuthStoreHealthCheckTests finally { if (File.Exists(path)) File.Delete(path); } } + /// Verifies that the health check reports Unhealthy when the store path cannot be opened. + /// A task that represents the asynchronous operation. [Fact] public async Task Unhealthy_WhenPathUnusable() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Diagnostics/GatewayLogRedactorSeamTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Diagnostics/GatewayLogRedactorSeamTests.cs index 494808e..abcbeaa 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Diagnostics/GatewayLogRedactorSeamTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Diagnostics/GatewayLogRedactorSeamTests.cs @@ -4,6 +4,7 @@ using Xunit; public class GatewayLogRedactorSeamTests { + /// Verifies that the log redactor masks the API key secret in the ClientIdentity field. [Fact] public void Redact_MasksApiKeyInClientIdentity() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyBrowseProjectorTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyBrowseProjectorTests.cs index 50a7d62..0eb860f 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyBrowseProjectorTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyBrowseProjectorTests.cs @@ -229,6 +229,7 @@ public sealed class GalaxyBrowseProjectorTests /// HasMatchingDescendant, the depth-first walk would loop forever; the /// 5-second xUnit timeout asserts termination. /// + /// A task that represents the asynchronous operation. [Fact(Timeout = 5000)] public async Task Project_CyclicDescendants_DoesNotInfiniteLoop() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyDeployNotifierTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyDeployNotifierTests.cs index 3324efa..a5caa80 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyDeployNotifierTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyDeployNotifierTests.cs @@ -5,6 +5,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Galaxy; public sealed class GalaxyDeployNotifierTests { /// Verifies that a subscriber blocks until a deploy event is published. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_NoLatestEvent_BlocksUntilPublish() { @@ -34,6 +35,7 @@ public sealed class GalaxyDeployNotifierTests } /// Verifies that a subscriber immediately receives a cached latest deploy event. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeAsync_WithLatestEvent_BootstrapsImmediately() { @@ -53,6 +55,7 @@ public sealed class GalaxyDeployNotifierTests } /// Verifies that published events fan out to all active subscribers. + /// A task that represents the asynchronous operation. [Fact] public async Task Publish_FansOutToAllSubscribers() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyFilterInputSafetyTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyFilterInputSafetyTests.cs index ab18465..766b28f 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyFilterInputSafetyTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyFilterInputSafetyTests.cs @@ -45,6 +45,7 @@ public sealed class GalaxyFilterInputSafetyTests ]; /// Returns adversarial input cases for theory tests. + /// Theory data containing adversarial input strings. public static TheoryData AdversarialInputCases() { TheoryData data = []; @@ -222,6 +223,7 @@ public sealed class GalaxyFilterInputSafetyTests /// zero matches rather than returning the whole hierarchy or faulting. /// /// An adversarial glob containing SQL metacharacters or LIKE wildcards. + /// A task that represents the asynchronous operation. [Theory] [MemberData(nameof(AdversarialInputCases))] public async Task DiscoverHierarchy_WithAdversarialTagNameGlob_ReturnsZeroMatches(string glob) @@ -242,6 +244,7 @@ public sealed class GalaxyFilterInputSafetyTests /// a query fragment or matching unrelated objects. /// /// An adversarial tag name containing SQL metacharacters or LIKE wildcards. + /// A task that represents the asynchronous operation. [Theory] [MemberData(nameof(AdversarialInputCases))] public async Task DiscoverHierarchy_WithAdversarialRootTagName_ReturnsNotFound(string rootTagName) diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyHierarchyCacheTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyHierarchyCacheTests.cs index 96dcb2a..1f9a805 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyHierarchyCacheTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyHierarchyCacheTests.cs @@ -34,6 +34,7 @@ public sealed class GalaxyHierarchyCacheTests : IDisposable /// connection failure, but it is fully covered by the cache's exception /// branch and does not require a real TCP probe from a unit test. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RefreshAsync_WhenRepositoryThrows_MarksUnavailableAndDoesNotPublish() { @@ -130,6 +131,7 @@ public sealed class GalaxyHierarchyCacheTests : IDisposable /// Verifies a successful refresh writes the browse dataset to the on-disk /// snapshot store so a later cold start can restore it. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RefreshAsync_WhenSuccessful_PersistsSnapshotToDisk() { @@ -155,6 +157,7 @@ public sealed class GalaxyHierarchyCacheTests : IDisposable /// snapshot exists on disk, the cache serves that data with Stale status /// rather than coming up empty. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RefreshAsync_WhenDatabaseUnreachableButSnapshotOnDisk_RestoresStaleData() { @@ -185,6 +188,7 @@ public sealed class GalaxyHierarchyCacheTests : IDisposable /// Galaxy database, the cache promotes the restored data to Healthy /// without re-running the heavy hierarchy and attribute queries. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RefreshAsync_WhenSnapshotDeployMatchesLive_PromotesToHealthyWithoutHeavyQuery() { @@ -216,6 +220,7 @@ public sealed class GalaxyHierarchyCacheTests : IDisposable /// the full bootstrap budget while the live Galaxy query is still running. /// Regression test for Server-033. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RefreshAsync_RestoredSnapshotCompletesFirstLoadBeforeLiveQueryReturns() { @@ -249,6 +254,7 @@ public sealed class GalaxyHierarchyCacheTests : IDisposable /// ignores the unreadable file and comes up Unavailable when the database is /// also unreachable. Regression test for Server-037. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RefreshAsync_WhenSnapshotFileCorrupt_ComesUpUnavailableWithoutThrowing() { @@ -271,6 +277,7 @@ public sealed class GalaxyHierarchyCacheTests : IDisposable /// restore from disk — an unreachable database leaves it Unavailable. /// Regression test for Server-037. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RefreshAsync_WhenPersistDisabled_DoesNotRestoreFromDisk() { @@ -291,6 +298,7 @@ public sealed class GalaxyHierarchyCacheTests : IDisposable /// (the refresh token is cancelled) is not logged as a persistence failure. /// Regression test for Server-036. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RefreshAsync_WhenSnapshotSaveCancelledAtShutdown_DoesNotLogPersistFailure() { @@ -402,14 +410,25 @@ public sealed class GalaxyHierarchyCacheTests : IDisposable /// Gets the list of recorded log entries. public List<(LogLevel Level, string Message)> Entries { get; } = []; - /// + /// Returns a no-op scope. + /// The type of state used for the scope. + /// The scope state. + /// A no-op disposable scope. public IDisposable BeginScope(TState state) where TState : notnull => NullScope.Instance; - /// + /// Returns for all log levels. + /// The log level to check. + /// . public bool IsEnabled(LogLevel logLevel) => true; - /// + /// Records the log entry. + /// The type of the state object. + /// The log level. + /// The event id. + /// The state object. + /// The exception, if any. + /// Delegate that formats the log message. public void Log( LogLevel logLevel, EventId eventId, @@ -424,7 +443,7 @@ public sealed class GalaxyHierarchyCacheTests : IDisposable { public static readonly NullScope Instance = new(); - /// + /// No-op dispose. public void Dispose() { } @@ -467,7 +486,7 @@ public sealed class GalaxyHierarchyCacheTests : IDisposable } } - /// + /// Deletes temporary snapshot files created during the test run. public void Dispose() { foreach (string path in _tempPaths) diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyHierarchyRefreshServiceTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyHierarchyRefreshServiceTests.cs index 7cf5045..a8a0ac9 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyHierarchyRefreshServiceTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyHierarchyRefreshServiceTests.cs @@ -14,6 +14,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Galaxy; public sealed class GalaxyHierarchyRefreshServiceTests { /// Verifies that the background service does not fault when the first refresh throws a non-cancellation exception. + /// A task that represents the asynchronous operation. [Fact] public async Task ExecuteAsync_WhenFirstRefreshThrowsNonCancellationException_DoesNotFaultBackgroundService() { @@ -69,11 +70,10 @@ public sealed class GalaxyHierarchyRefreshServiceTests /// Gets a task that completes once refresh has been invoked at least once. public Task FirstRefreshAttempted => firstRefreshAttempted.Task; - /// Gets the current cache entry. + /// public GalaxyHierarchyCacheEntry Current => GalaxyHierarchyCacheEntry.Empty; - /// Refreshes the cache asynchronously and throws the configured exception. - /// Token to observe for cancellation. + /// public Task RefreshAsync(CancellationToken cancellationToken) { RefreshCallCount++; @@ -81,8 +81,7 @@ public sealed class GalaxyHierarchyRefreshServiceTests throw toThrow; } - /// Waits for the first load and completes immediately. - /// Token to observe for cancellation. + /// public Task WaitForFirstLoadAsync(CancellationToken cancellationToken) => Task.CompletedTask; } } diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyHierarchySnapshotStoreTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyHierarchySnapshotStoreTests.cs index 6da3d34..6cfc03a 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyHierarchySnapshotStoreTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Galaxy/GalaxyHierarchySnapshotStoreTests.cs @@ -13,6 +13,7 @@ public sealed class GalaxyHierarchySnapshotStoreTests : IDisposable private readonly List _tempPaths = []; /// Verifies that snapshots are correctly saved to and loaded from disk. + /// A task that represents the asynchronous operation. [Fact] public async Task SaveAsync_ThenTryLoadAsync_RoundTripsRows() { @@ -41,6 +42,7 @@ public sealed class GalaxyHierarchySnapshotStoreTests : IDisposable } /// Verifies that loading returns null when no snapshot file exists. + /// A task that represents the asynchronous operation. [Fact] public async Task TryLoadAsync_WhenNoFileExists_ReturnsNull() { @@ -50,6 +52,7 @@ public sealed class GalaxyHierarchySnapshotStoreTests : IDisposable } /// Verifies that save writes nothing when persistence is disabled. + /// A task that represents the asynchronous operation. [Fact] public async Task SaveAsync_WhenPersistenceDisabled_WritesNothing() { @@ -63,6 +66,7 @@ public sealed class GalaxyHierarchySnapshotStoreTests : IDisposable } /// Verifies that loading returns null when the file contains invalid JSON. + /// A task that represents the asynchronous operation. [Fact] public async Task TryLoadAsync_WhenFileIsCorruptJson_ReturnsNull() { @@ -74,6 +78,7 @@ public sealed class GalaxyHierarchySnapshotStoreTests : IDisposable } /// Verifies that loading returns null when the schema version is unrecognized. + /// A task that represents the asynchronous operation. [Fact] public async Task TryLoadAsync_WhenSchemaVersionUnrecognized_ReturnsNull() { @@ -85,6 +90,7 @@ public sealed class GalaxyHierarchySnapshotStoreTests : IDisposable } /// Verifies that saving overwrites an earlier snapshot. + /// A task that represents the asynchronous operation. [Fact] public async Task SaveAsync_OverwritesAnEarlierSnapshot() { @@ -165,7 +171,7 @@ public sealed class GalaxyHierarchySnapshotStoreTests : IDisposable return path; } - /// + /// Deletes all temporary snapshot files created during the test. public void Dispose() { foreach (string path in _tempPaths) diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardApiKeyManagementServiceTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardApiKeyManagementServiceTests.cs index c27241d..1771951 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardApiKeyManagementServiceTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardApiKeyManagementServiceTests.cs @@ -29,6 +29,7 @@ public sealed class DashboardApiKeyManagementServiceTests : IDisposable private readonly List _tempDirectories = []; /// Verifies that unauthorized users cannot create API keys. + /// A task that represents the asynchronous operation. [Fact] public async Task CreateAsync_UnauthorizedUser_DoesNotCreate() { @@ -45,6 +46,7 @@ public sealed class DashboardApiKeyManagementServiceTests : IDisposable } /// Verifies that authorized users create a verifiable, constrained key and audit it. + /// A task that represents the asynchronous operation. [Fact] public async Task CreateAsync_AuthorizedUser_CreatesVerifiableKeyAndAudits() { @@ -79,6 +81,7 @@ public sealed class DashboardApiKeyManagementServiceTests : IDisposable } /// Verifies that creating a key whose id already exists is rejected. + /// A task that represents the asynchronous operation. [Fact] public async Task CreateAsync_DuplicateKeyId_ReportsConflict() { @@ -96,6 +99,7 @@ public sealed class DashboardApiKeyManagementServiceTests : IDisposable } /// Verifies that authorized users can revoke keys with audit trail. + /// A task that represents the asynchronous operation. [Fact] public async Task RevokeAsync_AuthorizedUser_RevokesAndAudits() { @@ -121,6 +125,7 @@ public sealed class DashboardApiKeyManagementServiceTests : IDisposable } /// Verifies that authorized users can rotate a key's secret with audit trail. + /// A task that represents the asynchronous operation. [Fact] public async Task RotateAsync_AuthorizedUser_RotatesAndAudits() { @@ -153,6 +158,7 @@ public sealed class DashboardApiKeyManagementServiceTests : IDisposable } /// Verifies that authorized users can delete revoked keys with audit trail. + /// A task that represents the asynchronous operation. [Fact] public async Task DeleteAsync_AuthorizedUser_DeletesRevokedKeyAndAudits() { @@ -181,6 +187,7 @@ public sealed class DashboardApiKeyManagementServiceTests : IDisposable /// dashboard-delete-key audit entry with Details = "not-found-or-active" is still /// written — audit completeness for refused deletes. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DeleteAsync_ActiveKey_ReportsFriendlyErrorAndAudits() { @@ -206,6 +213,7 @@ public sealed class DashboardApiKeyManagementServiceTests : IDisposable /// A blank key id fails validation before any store or audit call runs. /// A blank or whitespace key identifier. + /// A task that represents the asynchronous operation. [Theory] [InlineData("")] [InlineData(" ")] @@ -229,6 +237,7 @@ public sealed class DashboardApiKeyManagementServiceTests : IDisposable /// non-canonical scope string rather than persisting a key whose scope the authorization /// resolver never matches. /// + /// A task that represents the asynchronous operation. [Fact] public async Task CreateAsync_UnknownScope_DoesNotCreate() { @@ -255,6 +264,7 @@ public sealed class DashboardApiKeyManagementServiceTests : IDisposable /// Phase 3 canonical audit shape: the dashboard-create-key canonical AuditEvent records /// the operator username as Actor and the managed keyId as Target. /// + /// A task that represents the asynchronous operation. [Fact] public async Task CreateAsync_AuthorizedUser_CanonicalAuditEventHasOperatorAsActorAndKeyIdAsTarget() { @@ -386,7 +396,10 @@ public sealed class DashboardApiKeyManagementServiceTests : IDisposable /// Gets the recorded canonical audit events. public List Events { get; } = []; - /// + /// Records the audit event and returns a completed task. + /// The audit event to record. + /// Cancellation token (unused). + /// A task that represents the asynchronous operation. public Task WriteAsync(ZB.MOM.WW.Audit.AuditEvent auditEvent, CancellationToken cancellationToken = default) { Events.Add(auditEvent); diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardAuthenticatorTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardAuthenticatorTests.cs index 7cd03a6..91caae6 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardAuthenticatorTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardAuthenticatorTests.cs @@ -20,6 +20,8 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard; public sealed class DashboardAuthenticatorTests { /// A blank username is rejected without touching the LDAP provider. + /// A null, empty, or whitespace-only username. + /// A task that represents the asynchronous operation. [Theory] [InlineData(null)] [InlineData("")] @@ -40,6 +42,7 @@ public sealed class DashboardAuthenticatorTests } /// A blank password is rejected without touching the LDAP provider. + /// A task that represents the asynchronous operation. [Fact] public async Task AuthenticateAsync_BlankPassword_FailsWithoutCallingLdap() { @@ -60,6 +63,8 @@ public sealed class DashboardAuthenticatorTests /// A failed LDAP outcome (any failure bucket, including ) /// maps to the generic dashboard failure without leaking the raw password. /// + /// The LDAP failure kind to test. + /// A task that represents the asynchronous operation. [Theory] [InlineData(LdapAuthFailure.Disabled)] [InlineData(LdapAuthFailure.BadCredentials)] @@ -87,6 +92,7 @@ public sealed class DashboardAuthenticatorTests /// name (ClaimTypes.Name), and the username (ClaimTypes.NameIdentifier), under the /// dashboard authentication scheme. /// + /// A task that represents the asynchronous operation. [Fact] public async Task AuthenticateAsync_Success_BuildsPrincipalWithExpectedClaims() { @@ -132,6 +138,7 @@ public sealed class DashboardAuthenticatorTests /// ZbClaimTypes.DisplayName ("zb:displayname") with the display name — while keeping /// ClaimTypes.NameIdentifier, ClaimTypes.Name, and mxgateway:ldap_group claims intact. /// + /// A task that represents the asynchronous operation. [Fact] public async Task AuthenticateAsync_Success_EmitsCanonicalZbClaims() { @@ -183,6 +190,7 @@ public sealed class DashboardAuthenticatorTests /// When the user authenticates but none of their groups map to a dashboard role, /// the login is denied (the long-standing "no roles matched → denied" rule). /// + /// A task that represents the asynchronous operation. [Fact] public async Task AuthenticateAsync_NoRolesMatched_DeniesLogin() { @@ -216,6 +224,7 @@ public sealed class DashboardAuthenticatorTests /// for the realistic (already-short) group shape. /// /// + /// A task that represents the asynchronous operation. [Fact] public async Task AuthenticateAsync_GroupAsDistinguishedNameFromService_ResolvesRoleAndSurfacesServiceValue() { @@ -243,6 +252,7 @@ public sealed class DashboardAuthenticatorTests } /// The (already-trimmed) username from the LDAP result flows onto the principal. + /// A task that represents the asynchronous operation. [Fact] public async Task AuthenticateAsync_UsesUsernameFromLdapResult() { @@ -296,10 +306,17 @@ public sealed class DashboardAuthenticatorTests /// private sealed class FakeLdapAuthService(LdapAuthResult result) : ILdapAuthService { + /// Gets a value indicating whether AuthenticateAsync was called. public bool WasCalled { get; private set; } + /// Gets the username passed to the most recent AuthenticateAsync call. public string? LastUsername { get; private set; } + /// Records the call and returns the configured result. + /// The username passed by the authenticator. + /// The password passed by the authenticator. + /// Cancellation token. + /// A task that resolves to the configured LDAP authentication result. public Task AuthenticateAsync(string username, string password, CancellationToken ct) { WasCalled = true; diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardAuthorizationHandlerTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardAuthorizationHandlerTests.cs index 6dbf84a..9d4ab2d 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardAuthorizationHandlerTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardAuthorizationHandlerTests.cs @@ -11,6 +11,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard; public sealed class DashboardAuthorizationHandlerTests { /// Verifies that unauthenticated remote requests fail authorization. + /// A task that represents the asynchronous operation. [Fact] public async Task HandleAsync_UnauthenticatedRemoteRequest_DoesNotSucceed() { @@ -24,6 +25,7 @@ public sealed class DashboardAuthorizationHandlerTests } /// Verifies that anonymous localhost access succeeds when allowed. + /// A task that represents the asynchronous operation. [Fact] public async Task HandleAsync_AnonymousLocalhostAllowed_Succeeds() { @@ -40,6 +42,7 @@ public sealed class DashboardAuthorizationHandlerTests /// Verifies that the anonymous-localhost bypass is denied when AllowAnonymousLocalhost /// is off, even on a loopback connection — the misconfiguration must not expose the dashboard. /// + /// A task that represents the asynchronous operation. [Fact] public async Task HandleAsync_AnonymousLocalhostDisallowed_DoesNotSucceed() { @@ -56,6 +59,7 @@ public sealed class DashboardAuthorizationHandlerTests /// Verifies that the anonymous-localhost bypass stays scoped to loopback: an anonymous /// request from a non-loopback address is denied even when AllowAnonymousLocalhost is on. /// + /// A task that represents the asynchronous operation. [Fact] public async Task HandleAsync_AnonymousLocalhostAllowedFromRemoteAddress_DoesNotSucceed() { @@ -69,6 +73,7 @@ public sealed class DashboardAuthorizationHandlerTests } /// Verifies that an authenticated user without any dashboard role fails the viewer requirement. + /// A task that represents the asynchronous operation. [Fact] public async Task HandleAsync_AuthenticatedWithoutDashboardRole_DoesNotSucceed() { @@ -82,6 +87,7 @@ public sealed class DashboardAuthorizationHandlerTests } /// Verifies that a Viewer satisfies the viewer-or-admin requirement. + /// A task that represents the asynchronous operation. [Fact] public async Task HandleAsync_ViewerRole_SatisfiesViewerPolicy() { @@ -95,6 +101,7 @@ public sealed class DashboardAuthorizationHandlerTests } /// Verifies that an Admin satisfies the admin-only requirement. + /// A task that represents the asynchronous operation. [Fact] public async Task HandleAsync_AdminRole_SatisfiesAdminPolicy() { @@ -108,6 +115,7 @@ public sealed class DashboardAuthorizationHandlerTests } /// Verifies that a Viewer does NOT satisfy the admin-only requirement. + /// A task that represents the asynchronous operation. [Fact] public async Task HandleAsync_ViewerRole_DoesNotSatisfyAdminPolicy() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardCookieOptionsTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardCookieOptionsTests.cs index 95ed26c..557910c 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardCookieOptionsTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardCookieOptionsTests.cs @@ -11,6 +11,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard; public sealed class DashboardCookieOptionsTests { /// Verifies that the application configures secure dashboard authentication cookies. + /// A task that represents the asynchronous operation. [Fact] public async Task Build_ConfiguresSecureDashboardCookie() { @@ -36,6 +37,7 @@ public sealed class DashboardCookieOptionsTests /// relaxes the cookie to so /// the dashboard can be reached over plain HTTP in dev. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Build_WithRequireHttpsCookieFalse_UsesSameAsRequest() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardGroupRoleMapperTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardGroupRoleMapperTests.cs index 935f7c7..57a8240 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardGroupRoleMapperTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardGroupRoleMapperTests.cs @@ -35,6 +35,7 @@ public sealed class DashboardGroupRoleMapperTests /// Verifies full-DN match, leading-RDN fallback, case-insensitivity, and unmapped → empty. /// The LDAP group name or distinguished name. /// The expected role or null if no match. + /// A task that represents the asynchronous operation. [Theory] [InlineData("GwAdmin", DashboardRoles.Admin)] [InlineData("gwadmin", DashboardRoles.Admin)] @@ -61,6 +62,7 @@ public sealed class DashboardGroupRoleMapperTests } /// Verifies that admin and viewer roles are both emitted when both groups are present. + /// A task that represents the asynchronous operation. [Fact] public async Task MapAsync_AdminPlusViewer_BothRolesEmitted() { @@ -75,6 +77,7 @@ public sealed class DashboardGroupRoleMapperTests } /// Verifies that an empty GroupToRole map yields no roles. + /// A task that represents the asynchronous operation. [Fact] public async Task MapAsync_EmptyMapping_ReturnsNoRoles() { @@ -92,6 +95,7 @@ public sealed class DashboardGroupRoleMapperTests /// string LITERALS — independent of — so a /// regression on the constant's value is caught here. /// + /// A task that represents the asynchronous operation. [Fact] public async Task MapAsync_AdminGroup_ResolvesToCanonicalAdministratorValue() { @@ -124,6 +128,7 @@ public sealed class DashboardGroupRoleMapperTests /// Verifies the extracted shared helper is the single source of truth: it /// produces the same roles the mapper does for the same inputs. /// + /// A task that represents the asynchronous operation. [Fact] public async Task SharedHelper_MatchesMapperOutput() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardHubsRegistrationTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardHubsRegistrationTests.cs index 3348029..638dbd5 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardHubsRegistrationTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardHubsRegistrationTests.cs @@ -10,6 +10,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard; public sealed class DashboardHubsRegistrationTests { /// Verifies that dashboard build maps all three hubs and token endpoint. + /// A task that represents the asynchronous operation. [Fact] public async Task Build_WhenDashboardEnabled_MapsAllThreeHubsAndTokenEndpoint() { @@ -27,6 +28,7 @@ public sealed class DashboardHubsRegistrationTests } /// Verifies that dashboard build registers hub token service and connection factory. + /// A task that represents the asynchronous operation. [Fact] public async Task Build_WhenDashboardEnabled_RegistersHubTokenServiceAndConnectionFactory() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardSessionAdminServiceTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardSessionAdminServiceTests.cs index 5ffa1d0..6b6367b 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardSessionAdminServiceTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardSessionAdminServiceTests.cs @@ -10,6 +10,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard; public sealed class DashboardSessionAdminServiceTests { /// Verifies that a viewer cannot close a session. + /// A task that represents the asynchronous operation. [Fact] public async Task CloseSessionAsync_ViewerCannotManage() { @@ -26,6 +27,7 @@ public sealed class DashboardSessionAdminServiceTests } /// Verifies that an admin can close a session. + /// A task that represents the asynchronous operation. [Fact] public async Task CloseSessionAsync_AdminClosesSession() { @@ -43,6 +45,7 @@ public sealed class DashboardSessionAdminServiceTests } /// Verifies that closing a missing session returns a friendly error message. + /// A task that represents the asynchronous operation. [Fact] public async Task CloseSessionAsync_WhenSessionMissing_ReportsFriendlyError() { @@ -62,6 +65,7 @@ public sealed class DashboardSessionAdminServiceTests } /// Verifies that a viewer cannot kill a worker. + /// A task that represents the asynchronous operation. [Fact] public async Task KillWorkerAsync_ViewerCannotManage() { @@ -78,6 +82,7 @@ public sealed class DashboardSessionAdminServiceTests } /// Verifies that an admin can kill a worker. + /// A task that represents the asynchronous operation. [Fact] public async Task KillWorkerAsync_AdminKillsWorker() { @@ -101,6 +106,7 @@ public sealed class DashboardSessionAdminServiceTests } /// Verifies that killing a worker with a blank session ID returns failure. + /// A task that represents the asynchronous operation. [Fact] public async Task KillWorkerAsync_BlankSessionId_ReturnsFailure() { @@ -121,6 +127,7 @@ public sealed class DashboardSessionAdminServiceTests /// KillWorkerAsync but previously had no parallel test. Coverage was asymmetric. /// A guard-removal regression on the close path would slip through. /// + /// A task that represents the asynchronous operation. [Fact] public async Task CloseSessionAsync_BlankSessionId_ReturnsFailure() { @@ -154,6 +161,7 @@ public sealed class DashboardSessionAdminServiceTests /// must be converted to a friendly /// rather than propagating raw into Blazor's error boundary. /// + /// A task that represents the asynchronous operation. [Fact] public async Task CloseSessionAsync_WhenManagerThrowsUnexpected_ReturnsFriendlyFail() { @@ -175,6 +183,7 @@ public sealed class DashboardSessionAdminServiceTests /// /// Regression for Server-050: same friendly-fail contract for the Kill path. /// + /// A task that represents the asynchronous operation. [Fact] public async Task KillWorkerAsync_WhenManagerThrowsUnexpected_ReturnsFriendlyFail() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardSnapshotPublisherTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardSnapshotPublisherTests.cs index 6533a84..9b6b6e1 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardSnapshotPublisherTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardSnapshotPublisherTests.cs @@ -25,6 +25,7 @@ public sealed class DashboardSnapshotPublisherTests /// flaky on slow CI; recording firstThrowAt inside the fake removes /// that baseline so only the Task.Delay(reconnectDelay) contributes. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ExecuteAsync_WhenSnapshotServiceThrowsOnce_ReconnectsAfterDelay() { @@ -75,6 +76,7 @@ public sealed class DashboardSnapshotPublisherTests /// Sanity: a normal completion of WatchSnapshotsAsync (no exception) /// also reconnects after the delay — exits only on host shutdown. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ExecuteAsync_WhenSnapshotServiceCompletes_ReconnectsAfterDelay() { @@ -124,14 +126,13 @@ public sealed class DashboardSnapshotPublisherTests /// Gets the wall-clock instant of the second subscription attempt. public DateTimeOffset? SecondSubscribeAt { get; private set; } - /// Gets the current snapshot. + /// public DashboardSnapshot GetSnapshot() { return null!; } - /// Watches for snapshot changes and yields them asynchronously. - /// Token to observe for cancellation. + /// public async IAsyncEnumerable WatchSnapshotsAsync( [EnumeratorCancellation] CancellationToken cancellationToken) { @@ -167,14 +168,13 @@ public sealed class DashboardSnapshotPublisherTests /// Gets the number of subscription attempts. public int SubscribeCount { get; private set; } - /// Gets the current snapshot. + /// public DashboardSnapshot GetSnapshot() { return null!; } - /// Watches for snapshot changes and completes immediately. - /// Token to observe for cancellation. + /// #pragma warning disable CS1998 // async without await — IAsyncEnumerable contract requires async signature public async IAsyncEnumerable WatchSnapshotsAsync( [EnumeratorCancellation] CancellationToken cancellationToken) @@ -211,35 +211,43 @@ public sealed class DashboardSnapshotPublisherTests /// Gets a client proxy excluding specified connections. /// Connection identifiers to exclude. + /// A client proxy targeting all clients except the specified connections. public IClientProxy AllExcept(IReadOnlyList excludedConnectionIds) => AllProxy; /// Gets a client proxy for a specific connection. /// The connection identifier. + /// A client proxy targeting the specified connection. public IClientProxy Client(string connectionId) => AllProxy; /// Gets a client proxy for specified connections. /// The connection identifiers. + /// A client proxy targeting the specified connections. public IClientProxy Clients(IReadOnlyList connectionIds) => AllProxy; /// Gets a client proxy for a group. /// The group name. + /// A client proxy targeting the specified group. public IClientProxy Group(string groupName) => AllProxy; /// Gets a client proxy for a group excluding specified connections. /// The group name. /// Connection identifiers to exclude. + /// A client proxy targeting the group except the excluded connections. public IClientProxy GroupExcept(string groupName, IReadOnlyList excludedConnectionIds) => AllProxy; /// Gets a client proxy for specified groups. /// The group names. + /// A client proxy targeting the specified groups. public IClientProxy Groups(IReadOnlyList groupNames) => AllProxy; /// Gets a client proxy for a specific user. /// The user identifier. + /// A client proxy targeting the specified user. public IClientProxy User(string userId) => AllProxy; /// Gets a client proxy for specified users. /// The user identifiers. + /// A client proxy targeting the specified users. public IClientProxy Users(IReadOnlyList userIds) => AllProxy; } @@ -254,6 +262,7 @@ public sealed class DashboardSnapshotPublisherTests /// The SignalR method name. /// The method arguments. /// Token to observe for cancellation. + /// A task that represents the asynchronous operation. public Task SendCoreAsync(string method, object?[] args, CancellationToken cancellationToken = default) { Interlocked.Increment(ref _sendCount); @@ -267,6 +276,7 @@ public sealed class DashboardSnapshotPublisherTests /// The connection identifier. /// The group name. /// Token to observe for cancellation. + /// A task that represents the asynchronous operation. public Task AddToGroupAsync(string connectionId, string groupName, CancellationToken cancellationToken = default) => Task.CompletedTask; @@ -274,6 +284,7 @@ public sealed class DashboardSnapshotPublisherTests /// The connection identifier. /// The group name. /// Token to observe for cancellation. + /// A task that represents the asynchronous operation. public Task RemoveFromGroupAsync(string connectionId, string groupName, CancellationToken cancellationToken = default) => Task.CompletedTask; } diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardSnapshotServiceTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardSnapshotServiceTests.cs index a50b374..c43db95 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardSnapshotServiceTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardSnapshotServiceTests.cs @@ -269,6 +269,7 @@ public sealed class DashboardSnapshotServiceTests } /// Verifies that snapshot service refreshes API key summaries before each snapshot. + /// A task that represents the asynchronous operation. [Fact] public async Task WatchSnapshotsAsync_RefreshesApiKeySummariesBeforeSnapshot() { @@ -305,6 +306,7 @@ public sealed class DashboardSnapshotServiceTests } /// Verifies that snapshot service reuses previous summaries when API key refresh fails. + /// A task that represents the asynchronous operation. [Fact] public async Task WatchSnapshotsAsync_WhenApiKeyRefreshFails_ReusesPreviousSummaries() { @@ -348,6 +350,7 @@ public sealed class DashboardSnapshotServiceTests } /// Verifies that snapshot service disposes cleanly when subscriber cancels. + /// A task that represents the asynchronous operation. [Fact] public async Task WatchSnapshotsAsync_WhenSubscriberCancels_DisposesCleanly() { @@ -401,53 +404,59 @@ public sealed class DashboardSnapshotServiceTests private sealed class StubGalaxyHierarchyCache(GalaxyHierarchyCacheEntry current) : IGalaxyHierarchyCache { - /// - /// Gets the current Galaxy hierarchy cache entry. - /// + /// public GalaxyHierarchyCacheEntry Current { get; } = current; - /// - /// Refreshes the cache asynchronously. - /// - /// Cancellation token. - /// Completed task. + /// public Task RefreshAsync(CancellationToken cancellationToken) => Task.CompletedTask; - /// - /// Waits for the first cache load asynchronously. - /// - /// Cancellation token. - /// Completed task. + /// public Task WaitForFirstLoadAsync(CancellationToken cancellationToken) => Task.CompletedTask; } private class FakeApiKeyAdminStore : IApiKeyAdminStore { - /// + /// Stores the record; always succeeds. + /// The API key record to create. + /// Cancellation token. + /// A task that represents the asynchronous operation. public Task CreateAsync(ApiKeyRecord record, CancellationToken ct) { return Task.CompletedTask; } - /// + /// Returns an empty list of API key items. + /// Cancellation token. + /// A task that resolves to an empty read-only list. public virtual Task> ListAsync(CancellationToken ct) { return Task.FromResult>([]); } - /// + /// Marks the key revoked; always returns false (key not found). + /// The key identifier. + /// Revocation timestamp. + /// Cancellation token. + /// A task that resolves to false. public Task RevokeAsync(string keyId, DateTimeOffset whenUtc, CancellationToken ct) { return Task.FromResult(false); } - /// + /// Rotates the key secret; always returns false (key not found). + /// The key identifier. + /// The new secret hash bytes. + /// Cancellation token. + /// A task that resolves to false. public Task RotateAsync(string keyId, byte[] newSecretHash, CancellationToken ct) { return Task.FromResult(false); } - /// + /// Deletes the key; always returns false (key not found). + /// The key identifier. + /// Cancellation token. + /// A task that resolves to false. public Task DeleteAsync(string keyId, CancellationToken ct) { return Task.FromResult(false); @@ -512,24 +521,16 @@ public sealed class DashboardSnapshotServiceTests int? processId, WorkerClientState state) : IWorkerClient { - /// - /// Gets the session identifier. - /// + /// public string SessionId { get; } = sessionId; - /// - /// Gets the process identifier. - /// + /// public int? ProcessId { get; } = processId; - /// - /// Gets the current worker client state. - /// + /// public WorkerClientState State { get; private set; } = state; - /// - /// Gets the timestamp of the last heartbeat. - /// + /// public DateTimeOffset LastHeartbeatAt { get; } = DateTimeOffset.Parse("2026-04-26T10:02:00Z", CultureInfo.InvariantCulture); /// @@ -547,24 +548,14 @@ public sealed class DashboardSnapshotServiceTests /// public int KillCount { get; private set; } - /// - /// Starts the worker client asynchronously. - /// - /// Cancellation token. - /// Completed task. + /// public Task StartAsync(CancellationToken cancellationToken) { StartCount++; return Task.CompletedTask; } - /// - /// Invokes a worker command asynchronously. - /// - /// The command to invoke. - /// Command timeout. - /// Cancellation token. - /// Command reply. + /// public Task InvokeAsync( WorkerCommand command, TimeSpan timeout, @@ -573,11 +564,7 @@ public sealed class DashboardSnapshotServiceTests return Task.FromResult(new WorkerCommandReply()); } - /// - /// Reads events from the worker asynchronously. - /// - /// Cancellation token. - /// Async enumerable of worker events. + /// public async IAsyncEnumerable ReadEventsAsync( [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) { @@ -585,12 +572,7 @@ public sealed class DashboardSnapshotServiceTests yield break; } - /// - /// Shuts down the worker client asynchronously. - /// - /// Shutdown timeout. - /// Cancellation token. - /// Completed task. + /// public Task ShutdownAsync( TimeSpan timeout, CancellationToken cancellationToken) @@ -600,20 +582,15 @@ public sealed class DashboardSnapshotServiceTests return Task.CompletedTask; } - /// - /// Terminates the worker client. - /// - /// Reason for termination. + /// public void Kill(string reason) { KillCount++; State = WorkerClientState.Faulted; } - /// - /// Releases resources used by this worker client. - /// - /// Completed value task. + /// Disposes the fake worker client; no-op for testing. + /// A completed task. public ValueTask DisposeAsync() { return ValueTask.CompletedTask; diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayApplicationTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayApplicationTests.cs index 98ee67e..9b81458 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayApplicationTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayApplicationTests.cs @@ -15,6 +15,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway; public sealed class GatewayApplicationTests { /// Verifies that Build maps the canonical three health tiers. + /// A task that represents the asynchronous operation. [Fact] public async Task Build_MapsCanonicalHealthEndpoints() { @@ -42,6 +43,7 @@ public sealed class GatewayApplicationTests } /// Verifies that Build registers the gateway metrics service. + /// A task that represents the asynchronous operation. [Fact] public async Task Build_RegistersGatewayMetrics() { @@ -53,6 +55,7 @@ public sealed class GatewayApplicationTests } /// Verifies that Build mounts the Prometheus /metrics scrape endpoint. + /// A task that represents the asynchronous operation. [Fact] public async Task Build_MapsMetricsEndpoint() { @@ -75,6 +78,7 @@ public sealed class GatewayApplicationTests } /// Verifies that Build maps dashboard and authentication endpoints when the dashboard is enabled. + /// A task that represents the asynchronous operation. [Fact] public async Task Build_WhenDashboardEnabled_MapsBlazorDashboardAndAuthEndpoints() { @@ -99,6 +103,7 @@ public sealed class GatewayApplicationTests } /// Verifies that the dashboard login, logout, and denied endpoints allow anonymous access. + /// A task that represents the asynchronous operation. [Fact] public async Task Build_WhenDashboardEnabled_AuthEndpointsAllowAnonymousAccess() { @@ -128,6 +133,7 @@ public sealed class GatewayApplicationTests } /// Verifies that dashboard Razor component routes require the dashboard viewer policy. + /// A task that represents the asynchronous operation. [Fact] public async Task Build_WhenDashboardEnabled_ComponentRoutesRequireAuthorization() { @@ -156,6 +162,7 @@ public sealed class GatewayApplicationTests } /// Verifies that dashboard routes are registered at root when enabled. + /// A task that represents the asynchronous operation. [Fact] public async Task Build_WhenDashboardEnabled_RegistersDashboardRoutesAtRoot() { @@ -181,6 +188,7 @@ public sealed class GatewayApplicationTests } /// Verifies that dashboard routes are not mapped when disabled. + /// A task that represents the asynchronous operation. [Fact] public async Task Build_WhenDashboardDisabled_DoesNotMapDashboardRoutes() { @@ -197,6 +205,7 @@ public sealed class GatewayApplicationTests /// Configuration key to override. /// Invalid configuration value. /// Expected validation error message. + /// A task that represents the asynchronous operation. [Theory] [InlineData( "MxGateway:Worker:ExecutablePath", diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayEndToEndFakeWorkerSmokeTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayEndToEndFakeWorkerSmokeTests.cs index 2185f29..1c53116 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayEndToEndFakeWorkerSmokeTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayEndToEndFakeWorkerSmokeTests.cs @@ -25,6 +25,7 @@ public sealed class GatewayEndToEndFakeWorkerSmokeTests /// /// Verifies gateway session lifecycle with a scripted fake worker: open, command, event, close. /// + /// A task that represents the asynchronous operation. [Fact] public async Task GatewayService_WithFakeWorker_CompletesSessionCommandEventAndClosePath() { @@ -201,6 +202,7 @@ public sealed class GatewayEndToEndFakeWorkerSmokeTests /// /// Disposes all active sessions and metrics. /// + /// A value task that represents the asynchronous disposal. public async ValueTask DisposeAsync() { foreach (GatewaySession session in _registry.Snapshot()) @@ -256,12 +258,7 @@ public sealed class GatewayEndToEndFakeWorkerSmokeTests /// public Task WorkerTask { get; private set; } = Task.CompletedTask; - /// - /// Launches a new worker process and returns a handle to manage it. - /// - /// Worker process launch request parameters. - /// Cancellation token. - /// Worker process handle. + /// public Task LaunchAsync( WorkerProcessLaunchRequest request, CancellationToken cancellationToken = default) @@ -359,37 +356,22 @@ public sealed class GatewayEndToEndFakeWorkerSmokeTests { private readonly TaskCompletionSource _exited = new(TaskCreationOptions.RunContinuationsAsynchronously); - /// - /// Gets the process identifier. - /// + /// public int Id { get; } = processId; - /// - /// Gets a value indicating whether the process has exited. - /// + /// public bool HasExited { get; private set; } - /// - /// Gets the exit code of the process. - /// + /// public int? ExitCode { get; private set; } - /// - /// Waits for the process to exit asynchronously. Completes only when - /// or has been called, so callers that observe completion can - /// trust that exit actually happened (e.g., via the worker shutdown-ack path). - /// - /// Cancellation token. - /// A task that completes when the process has actually exited. + /// public ValueTask WaitForExitAsync(CancellationToken cancellationToken) { return new ValueTask(_exited.Task.WaitAsync(cancellationToken)); } - /// - /// Terminates the process. - /// - /// Whether to kill the entire process tree. + /// public void Kill(bool entireProcessTree) { MarkExited(-1); diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayTlsBootstrapTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayTlsBootstrapTests.cs index f34d698..38cc3c9 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayTlsBootstrapTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayTlsBootstrapTests.cs @@ -21,6 +21,7 @@ public sealed class GatewayTlsBootstrapTests /// trusted dev cert, Kestrel would otherwise serve that dev cert (CN=localhost), so the /// subject assertion is what makes this test fail without the wiring on either kind of host. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Host_ServesGeneratedCertificate_WhenHttpsEndpointHasNoCertificate() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/EventStreamServiceTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/EventStreamServiceTests.cs index 6f45a15..f7af36f 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/EventStreamServiceTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/EventStreamServiceTests.cs @@ -18,6 +18,7 @@ public sealed class EventStreamServiceTests private static readonly TimeSpan TestTimeout = TimeSpan.FromSeconds(5); /// Verifies that events from the worker stream maintain their original sequence order. + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEventsAsync_YieldsEventsInWorkerOrder() { @@ -39,6 +40,7 @@ public sealed class EventStreamServiceTests } /// Verifies that a second event subscriber is rejected when one is already active. + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEventsAsync_WhenSecondSubscriberStarts_RejectsClearly() { @@ -68,6 +70,7 @@ public sealed class EventStreamServiceTests } /// Verifies that canceling an event stream detaches the subscriber cleanly. + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEventsAsync_WhenCanceled_DetachesSubscriber() { @@ -90,6 +93,7 @@ public sealed class EventStreamServiceTests } /// Verifies that disposing an event stream with buffered events resets the queue depth metric. + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEventsAsync_WhenDisposedWithBufferedEvents_ResetsStreamQueueDepth() { @@ -117,6 +121,7 @@ public sealed class EventStreamServiceTests } /// Verifies that queue depth metrics correctly track concurrent event streams across multiple sessions. + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEventsAsync_WithConcurrentStreams_TracksAggregateQueueDepth() { @@ -158,6 +163,7 @@ public sealed class EventStreamServiceTests } /// Verifies that event queue overflow faults the session and reports the overflow metric. + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEventsAsync_WhenStreamQueueOverflows_FaultsSessionAndReportsOverflow() { @@ -188,6 +194,7 @@ public sealed class EventStreamServiceTests } /// Verifies that the disconnect backpressure policy disconnects the subscriber without faulting the session. + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEventsAsync_WhenStreamQueueOverflowsWithDisconnectPolicy_LeavesSessionReady() { @@ -220,6 +227,7 @@ public sealed class EventStreamServiceTests } /// Verifies that the event stream does not synthesize OperationComplete events from write completions. + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEventsAsync_DoesNotSynthesizeOperationComplete() { @@ -237,6 +245,7 @@ public sealed class EventStreamServiceTests } /// Verifies that a terminal fault from the worker event stream propagates and faults the session. + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEventsAsync_WhenWorkerEventStreamFaults_PropagatesTerminalFault() { @@ -273,6 +282,7 @@ public sealed class EventStreamServiceTests /// assert one publish per yielded event, with the correct session id and /// preserved WorkerSequence. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEventsAsync_PublishesEachEventToDashboardBroadcaster() { @@ -308,6 +318,7 @@ public sealed class EventStreamServiceTests /// surface the exception and the client would see a faulted stream /// for a dashboard-mirror failure. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEventsAsync_WhenDashboardBroadcasterThrows_StillYieldsEventsAndDoesNotFaultSession() { @@ -358,9 +369,7 @@ public sealed class EventStreamServiceTests /// Gets the count of publish attempts. public int PublishAttempts { get; private set; } - /// Increments the attempt count and throws a simulated failure. - /// The session identifier. - /// The event to publish. + /// public void Publish(string sessionId, MxEvent mxEvent) { PublishAttempts++; @@ -610,7 +619,8 @@ public sealed class EventStreamServiceTests State = WorkerClientState.Faulted; } - /// + /// Disposes the fake worker client. + /// A completed value task. public ValueTask DisposeAsync() { return ValueTask.CompletedTask; diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/GalaxyRepositoryGrpcServiceTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/GalaxyRepositoryGrpcServiceTests.cs index ee05cb9..d5ddd06 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/GalaxyRepositoryGrpcServiceTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/GalaxyRepositoryGrpcServiceTests.cs @@ -13,6 +13,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Grpc; public sealed class GalaxyRepositoryGrpcServiceTests { /// Verifies that DiscoverHierarchy returns the requested page and totals. + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverHierarchy_ReturnsRequestedPageAndTotals() { @@ -34,6 +35,7 @@ public sealed class GalaxyRepositoryGrpcServiceTests } /// Verifies that DiscoverHierarchy with a page token returns remaining objects. + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverHierarchy_WithNextPageToken_ReturnsRemainingObjects() { @@ -62,6 +64,7 @@ public sealed class GalaxyRepositoryGrpcServiceTests /// Verifies that DiscoverHierarchy with invalid paging arguments returns InvalidArgument. /// The page token to test. /// The page size to test. + /// A task that represents the asynchronous operation. [Theory] [InlineData("-1", 1)] [InlineData("not-an-offset", 1)] @@ -87,6 +90,7 @@ public sealed class GalaxyRepositoryGrpcServiceTests } /// Verifies that DiscoverHierarchy with subtree root and depth filters descendants. + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverHierarchy_WithSubtreeRootAndDepth_FiltersDescendants() { @@ -106,6 +110,7 @@ public sealed class GalaxyRepositoryGrpcServiceTests } /// Verifies that DiscoverHierarchy applies server-side filters and omits attributes. + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverHierarchy_WithServerSideFilters_AppliesAllFiltersAndOmitsAttributes() { @@ -132,6 +137,7 @@ public sealed class GalaxyRepositoryGrpcServiceTests } /// Verifies that DiscoverHierarchy with filtered paging returns post-filter total. + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverHierarchy_WithFilteredPaging_ReturnsPostFilterTotal() { @@ -164,6 +170,7 @@ public sealed class GalaxyRepositoryGrpcServiceTests } /// Verifies that DiscoverHierarchy with mismatched filter token returns InvalidArgument. + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverHierarchy_WithMismatchedFilterToken_ReturnsInvalidArgument() { @@ -191,6 +198,7 @@ public sealed class GalaxyRepositoryGrpcServiceTests } /// Verifies that DiscoverHierarchy with missing root returns NotFound. + /// A task that represents the asynchronous operation. [Fact] public async Task DiscoverHierarchy_WithMissingRoot_ReturnsNotFound() { @@ -337,6 +345,7 @@ public sealed class GalaxyRepositoryGrpcServiceTests } /// Verifies that BrowseChildren returns root objects and the current cache sequence when called with no parent. + /// A task that represents the asynchronous operation. [Fact] public async Task BrowseChildren_RootCall_ReturnsRootsWithCacheSequence() { @@ -355,6 +364,7 @@ public sealed class GalaxyRepositoryGrpcServiceTests } /// Verifies that BrowseChildren returns Unavailable when the cache's first load never completes. + /// A task that represents the asynchronous operation. [Fact] public async Task BrowseChildren_FirstLoadNotComplete_ReturnsUnavailable() { @@ -381,6 +391,7 @@ public sealed class GalaxyRepositoryGrpcServiceTests } /// Verifies that a page token bound to a stale cache sequence is rejected with InvalidArgument. + /// A task that represents the asynchronous operation. [Fact] public async Task BrowseChildren_StaleToken_ReturnsInvalidArgument() { @@ -407,6 +418,7 @@ public sealed class GalaxyRepositoryGrpcServiceTests } /// Verifies that switching filters between paged BrowseChildren calls is rejected. + /// A task that represents the asynchronous operation. [Fact] public async Task BrowseChildren_FilterChangeBetweenPages_ReturnsInvalidArgument() { @@ -436,6 +448,7 @@ public sealed class GalaxyRepositoryGrpcServiceTests } /// Verifies that an ApiKeyIdentity browse-subtrees constraint that matches nothing produces an empty child list. + /// A task that represents the asynchronous operation. [Fact] public async Task BrowseChildren_BrowseSubtreesConstraint_FiltersChildren() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/MxAccessGatewayServiceConstraintTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/MxAccessGatewayServiceConstraintTests.cs index a01cfb8..4fad5fe 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/MxAccessGatewayServiceConstraintTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/MxAccessGatewayServiceConstraintTests.cs @@ -34,6 +34,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// worker once with only the allowed tags, then splice the denied entries /// back into the reply at their original indices. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_AddItemBulk_WithMixedDenials_InterleavesDeniedAndAllowedInOriginalIndexOrder() { @@ -95,6 +96,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// false, return the /// denied-only reply, and never call the session manager. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_SubscribeBulk_WhenAllTagsDenied_DoesNotCallWorkerAndReturnsDeniedReply() { @@ -118,6 +120,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// FilterHandleBulkAsync against CheckReadHandleAsync. Partial /// denial must still produce a merged-by-index BulkSubscribeReply. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_AdviseItemBulk_WithMixedHandleDenials_MergesDeniedIntoReply() { @@ -166,6 +169,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// unchanged — the constraint plan is null and no merge occurs. Regression /// guard against accidentally engaging the merge path for the common case. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_SubscribeBulk_WithAllowAllEnforcer_PassesThroughUnchanged() { @@ -208,6 +212,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// the SubscribeBulk family because the reply slot is /// BulkReadReply, not BulkSubscribeReply. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_ReadBulk_WithMixedDenials_MergesDeniedBulkReadResults() { @@ -255,6 +260,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// 's ReadBulkConstraintPlan /// CreateDeniedReply path. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_ReadBulk_WhenAllTagsDenied_ShortCircuitsWithDeniedOnlyReply() { @@ -279,6 +285,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// forwarded command and splice a denied BulkWriteResult back in at /// the original index. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_WriteBulk_WithDeniedHandle_DropsEntryFromWorkerCallAndMergesDenialIntoReply() { @@ -329,6 +336,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// switch arm than plain WriteBulk. The merge logic is shared, so a /// full denial here is enough to prove the secured-bulk routing. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_WriteSecuredBulk_WhenAllHandlesDenied_ShortCircuitsWithDeniedOnlyReply() { @@ -354,6 +362,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// (and not, say, WriteBulk by mistake) — guarding against a refactor that /// drops or misroutes the Write2Bulk case. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_Write2Bulk_WhenAllHandlesDenied_ShortCircuitsWithDeniedOnlyReply() { @@ -382,6 +391,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// Write2Bulk — assert the WriteSecured2Bulk reply slot is populated /// to prove that arm of the switch fires. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_WriteSecured2Bulk_WhenAllHandlesDenied_ShortCircuitsWithDeniedOnlyReply() { @@ -417,6 +427,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// This fixture makes that "silent truncate" behaviour explicit so a future /// change either fills the gap with a synthetic failure or fails this test. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_WriteBulk_WhenWorkerReturnsFewerResultsThanAllowed_MergedReplyIsTruncated() { @@ -470,6 +481,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// for index < OriginalCount loop bound so a regression that /// accidentally surfaces extras as trailing results is caught. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_WriteBulk_WhenWorkerReturnsExtraResults_IgnoresExtras() { @@ -527,6 +539,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// via EnforceWriteHandleAsync /// and never reach the session manager. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_Write_WithDeniedHandle_ThrowsPermissionDeniedAndDoesNotCallWorker() { @@ -554,6 +567,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// ApplyConstraintsAsync (Write/Write2/WriteSecured/WriteSecured2) is /// reachable for at least one of the secured kinds. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_WriteSecured_WithDeniedHandle_ThrowsPermissionDenied() { @@ -577,6 +591,7 @@ public sealed class MxAccessGatewayServiceConstraintTests /// via EnforceReadTagAsync /// and never reach the session manager. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_AddItem_WithDeniedTag_ThrowsPermissionDeniedAndDoesNotCallWorker() { @@ -854,19 +869,14 @@ public sealed class MxAccessGatewayServiceConstraintTests /// The session to seed. public void SeedSession(GatewaySession session) => seededSessions[session.SessionId] = session; - /// Opens a test session asynchronously. - /// The session open request. - /// The client identity, if any. - /// Token to observe for cancellation. + /// public Task OpenSessionAsync( SessionOpenRequest request, string? clientIdentity, CancellationToken cancellationToken) => Task.FromResult(seededSessions.Values.First()); - /// Tries to get a test session by identifier. - /// The session identifier. - /// The session, if found. + /// public bool TryGetSession(string sessionId, out GatewaySession session) { if (seededSessions.TryGetValue(sessionId, out GatewaySession? seeded)) @@ -885,10 +895,7 @@ public sealed class MxAccessGatewayServiceConstraintTests return true; } - /// Invokes a worker command and returns the reply asynchronously. - /// The session identifier. - /// The worker command. - /// Token to observe for cancellation. + /// public Task InvokeAsync( string sessionId, WorkerCommand command, @@ -899,9 +906,7 @@ public sealed class MxAccessGatewayServiceConstraintTests return Task.FromResult(InvokeReply); } - /// Reads events from the session asynchronously. - /// The session identifier. - /// Token to observe for cancellation. + /// public async IAsyncEnumerable ReadEventsAsync( string sessionId, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) @@ -914,33 +919,25 @@ public sealed class MxAccessGatewayServiceConstraintTests } } - /// Closes a test session asynchronously. - /// The session identifier. - /// Token to observe for cancellation. + /// public Task CloseSessionAsync( string sessionId, CancellationToken cancellationToken) => Task.FromResult(new SessionCloseResult(sessionId, SessionState.Closed, AlreadyClosed: false)); - /// Kills a worker process asynchronously. - /// The session identifier. - /// The reason for killing the worker. - /// Token to observe for cancellation. + /// public Task KillWorkerAsync( string sessionId, string reason, CancellationToken cancellationToken) => Task.FromResult(new SessionCloseResult(sessionId, SessionState.Closed, AlreadyClosed: false)); - /// Closes expired session leases asynchronously. - /// The current time to check against. - /// Token to observe for cancellation. + /// public Task CloseExpiredLeasesAsync( DateTimeOffset now, CancellationToken cancellationToken) => Task.FromResult(0); - /// Shuts down the test session manager asynchronously. - /// Token to observe for cancellation. + /// public Task ShutdownAsync(CancellationToken cancellationToken) => Task.CompletedTask; private static GatewaySession CreateFallbackSession(string sessionId) @@ -965,9 +962,7 @@ public sealed class MxAccessGatewayServiceConstraintTests private sealed class FakeEventStreamService(FakeSessionManager sessionManager) : IEventStreamService { - /// Streams events for the test session asynchronously. - /// The stream events request. - /// Token to observe for cancellation. + /// public async IAsyncEnumerable StreamEventsAsync( StreamEventsRequest request, [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) @@ -983,33 +978,28 @@ public sealed class MxAccessGatewayServiceConstraintTests private sealed class FakeWorkerClient : IWorkerClient { - /// Gets the test session identifier. + /// public string SessionId { get; } = MxAccessGatewayServiceConstraintTests.SessionId; - /// Gets the test worker process identifier. + /// public int? ProcessId { get; } = 1234; - /// Gets the test worker client state. + /// public WorkerClientState State { get; } = WorkerClientState.Ready; - /// Gets the last recorded heartbeat time. + /// public DateTimeOffset LastHeartbeatAt { get; } = DateTimeOffset.UtcNow; - /// Starts the test worker client asynchronously. - /// Token to observe for cancellation. + /// public Task StartAsync(CancellationToken cancellationToken) => Task.CompletedTask; - /// Invokes a command on the test worker asynchronously. - /// The worker command. - /// Maximum time to wait for completion. - /// Token to observe for cancellation. + /// public Task InvokeAsync( WorkerCommand command, TimeSpan timeout, CancellationToken cancellationToken) => Task.FromResult(new WorkerCommandReply()); - /// Reads events from the test worker asynchronously. - /// Token to observe for cancellation. + /// public async IAsyncEnumerable ReadEventsAsync( [System.Runtime.CompilerServices.EnumeratorCancellation] CancellationToken cancellationToken) { @@ -1017,18 +1007,16 @@ public sealed class MxAccessGatewayServiceConstraintTests yield break; } - /// Shuts down the test worker client asynchronously. - /// Maximum time to wait for completion. - /// Token to observe for cancellation. + /// public Task ShutdownAsync(TimeSpan timeout, CancellationToken cancellationToken) => Task.CompletedTask; - /// Kills the test worker process. - /// The reason for killing the worker. + /// public void Kill(string reason) { } - /// + /// Releases resources held by this fake worker client asynchronously. + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } } diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/MxAccessGatewayServiceTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/MxAccessGatewayServiceTests.cs index cacda2e..9eac234 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/MxAccessGatewayServiceTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Grpc/MxAccessGatewayServiceTests.cs @@ -18,6 +18,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Grpc; public sealed class MxAccessGatewayServiceTests { /// Verifies that OpenSession returns correct session details for a valid request. + /// A task that represents the asynchronous operation. [Fact] public async Task OpenSession_WithValidRequest_ReturnsSessionDetails() { @@ -55,6 +56,7 @@ public sealed class MxAccessGatewayServiceTests /// TryGetSession return false, so this test fails if the service drops /// its missing-session check rather than passing for the wrong reason. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_WhenSessionMissing_ThrowsNotFound() { @@ -77,6 +79,7 @@ public sealed class MxAccessGatewayServiceTests /// manager when is on, /// confirming the missing-session test above is gated on a real lookup. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_WhenSessionSeeded_ResolvesAndInvokes() { @@ -93,6 +96,7 @@ public sealed class MxAccessGatewayServiceTests } /// Verifies that Invoke throws InvalidArgument and does not invoke the session manager when payload is mismatched. + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_WithMismatchedPayload_ThrowsInvalidArgumentAndDoesNotCallSessionManager() { @@ -116,6 +120,7 @@ public sealed class MxAccessGatewayServiceTests } /// Verifies that Invoke returns HResult status and method payload from worker reply. + /// A task that represents the asynchronous operation. [Fact] public async Task Invoke_WithWorkerReply_ReturnsHresultStatusAndMethodPayload() { @@ -171,6 +176,7 @@ public sealed class MxAccessGatewayServiceTests } /// Verifies that StreamEvents writes only events after the specified worker sequence. + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEvents_WithAfterSequence_WritesOnlyLaterEvents() { @@ -205,6 +211,7 @@ public sealed class MxAccessGatewayServiceTests /// /// regression for the cross-talk reproduction. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEvents_WhenEventIsWritten_RecordsSendDuration() { @@ -258,6 +265,7 @@ public sealed class MxAccessGatewayServiceTests /// caused StreamEvents_WhenEventIsWritten_RecordsSendDuration to fail intermittently when /// run in parallel with another test recording the same histogram. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StreamEvents_RecordSendDurationListener_IgnoresMeasurementsFromOtherMetersWithSameName() { @@ -314,6 +322,7 @@ public sealed class MxAccessGatewayServiceTests } /// Verifies that CloseSession throws InvalidArgument when session ID is blank. + /// A task that represents the asynchronous operation. [Fact] public async Task CloseSession_WithBlankSessionId_ThrowsInvalidArgument() { @@ -334,6 +343,7 @@ public sealed class MxAccessGatewayServiceTests // alarm feed. CreateService injects FakeGatewayAlarmService. /// Verifies AcknowledgeAlarm rejects an empty alarm_full_reference. + /// A task that represents the asynchronous operation. [Fact] public async Task AcknowledgeAlarm_WithMissingAlarmReference_ThrowsInvalidArgument() { @@ -348,6 +358,7 @@ public sealed class MxAccessGatewayServiceTests } /// Verifies AcknowledgeAlarm delegates a valid request to the alarm service. + /// A task that represents the asynchronous operation. [Fact] public async Task AcknowledgeAlarm_WithValidRequest_DelegatesToAlarmService() { @@ -368,6 +379,7 @@ public sealed class MxAccessGatewayServiceTests } /// Verifies StreamAlarms forwards the central alarm feed, ending with snapshot_complete. + /// A task that represents the asynchronous operation. [Fact] public async Task StreamAlarms_ForwardsTheCentralAlarmFeed() { @@ -385,6 +397,7 @@ public sealed class MxAccessGatewayServiceTests } /// Verifies OpenSession advertises the alarm RPC capability strings. + /// A task that represents the asynchronous operation. [Fact] public async Task OpenSession_AdvertisesAlarmRpcCapabilities() { @@ -709,7 +722,8 @@ public sealed class MxAccessGatewayServiceTests { } - /// + /// Releases resources held by this fake worker client. + /// A completed value task. public ValueTask DisposeAsync() { return ValueTask.CompletedTask; diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/GatewaySessionTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/GatewaySessionTests.cs index 403e532..d2e53c0 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/GatewaySessionTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/GatewaySessionTests.cs @@ -21,6 +21,7 @@ public sealed class GatewaySessionTests /// Closing write and the Closed write, which is precisely the /// window the audit identified. /// + /// A task that represents the asynchronous operation. [Fact] public async Task TransitionTo_AfterCloseStarted_DoesNotOverwriteClosing() { @@ -53,6 +54,7 @@ public sealed class GatewaySessionTests /// terminal write goes through the same _syncRoot the rest of the state /// machine uses, so the existing "Closed is terminal" invariant holds. /// + /// A task that represents the asynchronous operation. [Fact] public async Task MarkFaulted_AfterCloseCompletes_DoesNotResurrectSession() { @@ -79,6 +81,7 @@ public sealed class GatewaySessionTests /// Faulted" — this test pins the resolved end state so a future tightening /// of MarkFaulted cannot silently regress it. /// + /// A task that represents the asynchronous operation. [Fact] public async Task MarkFaulted_DuringInFlightClose_PreservesFaultButYieldsToClose() { @@ -111,6 +114,7 @@ public sealed class GatewaySessionTests /// its semaphore. Without the fix, the close's _closeLock.Release() /// would race the dispose and raise . /// + /// A task that represents the asynchronous operation. [Fact] public async Task DisposeAsync_WhileCloseInFlight_WaitsForCloseAndDoesNotThrow() { @@ -142,6 +146,7 @@ public sealed class GatewaySessionTests /// from the already-disposed semaphore /// rather than propagating it. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DisposeAsync_CalledTwice_DoesNotThrow() { @@ -187,16 +192,16 @@ public sealed class GatewaySessionTests private readonly TaskCompletionSource _shutdownStarted = new(TaskCreationOptions.RunContinuationsAsynchronously); private readonly TaskCompletionSource _shutdownReleased = new(TaskCreationOptions.RunContinuationsAsynchronously); - /// Gets the session identifier. + /// public string SessionId { get; } = "session-test"; - /// Gets the worker process identifier. + /// public int? ProcessId { get; } = 1234; - /// Gets or sets the worker client state. + /// public WorkerClientState State { get; private set; } = WorkerClientState.Ready; - /// Gets the last recorded heartbeat timestamp. + /// public DateTimeOffset LastHeartbeatAt { get; } = DateTimeOffset.UtcNow; /// Gets the count of shutdown invocations. @@ -206,6 +211,7 @@ public sealed class GatewaySessionTests public int DisposeCount { get; private set; } /// Waits for shutdown to start. + /// A task that represents the asynchronous operation. public Task WaitForShutdownStartAsync() { return _shutdownStarted.Task.WaitAsync(TimeSpan.FromSeconds(5)); @@ -249,7 +255,8 @@ public sealed class GatewaySessionTests State = WorkerClientState.Faulted; } - /// + /// Increments the dispose count and completes asynchronously. + /// A completed value task. public ValueTask DisposeAsync() { DisposeCount++; @@ -259,16 +266,16 @@ public sealed class GatewaySessionTests private sealed class FakeWorkerClient : IWorkerClient { - /// Gets the session identifier. + /// public string SessionId { get; } = "session-test"; - /// Gets the worker process identifier. + /// public int? ProcessId { get; } = 1234; - /// Gets the worker client state. + /// public WorkerClientState State { get; } = WorkerClientState.Ready; - /// Gets the last recorded heartbeat timestamp. + /// public DateTimeOffset LastHeartbeatAt { get; } = DateTimeOffset.UtcNow; /// Gets the count of dispose invocations. @@ -299,7 +306,8 @@ public sealed class GatewaySessionTests { } - /// + /// Increments the dispose count and completes asynchronously. + /// A completed value task. public ValueTask DisposeAsync() { DisposeCount++; diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionManagerBulkTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionManagerBulkTests.cs index d5fd7d4..c5567bb 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionManagerBulkTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionManagerBulkTests.cs @@ -21,6 +21,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Sessions; public sealed class SessionManagerBulkTests { /// Verifies that AddItemBulkAsync forwards the command and returns results. + /// A task that represents the asynchronous operation. [Fact] public async Task AddItemBulkAsync_ForwardsOneAddItemBulkCommandAndReturnsResults() { @@ -50,6 +51,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that AddItemBulkAsync propagates cancellation. + /// A task that represents the asynchronous operation. [Fact] public async Task AddItemBulkAsync_PropagatesCancellation() { @@ -63,6 +65,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that AdviseItemBulkAsync forwards the command and returns results. + /// A task that represents the asynchronous operation. [Fact] public async Task AdviseItemBulkAsync_ForwardsOneAdviseItemBulkCommandAndReturnsResults() { @@ -90,6 +93,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that AdviseItemBulkAsync propagates cancellation. + /// A task that represents the asynchronous operation. [Fact] public async Task AdviseItemBulkAsync_PropagatesCancellation() { @@ -103,6 +107,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that RemoveItemBulkAsync forwards the command and returns results. + /// A task that represents the asynchronous operation. [Fact] public async Task RemoveItemBulkAsync_ForwardsOneRemoveItemBulkCommandAndReturnsResults() { @@ -128,6 +133,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that RemoveItemBulkAsync propagates cancellation. + /// A task that represents the asynchronous operation. [Fact] public async Task RemoveItemBulkAsync_PropagatesCancellation() { @@ -141,6 +147,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that UnAdviseItemBulkAsync forwards the command and returns results. + /// A task that represents the asynchronous operation. [Fact] public async Task UnAdviseItemBulkAsync_ForwardsOneUnAdviseItemBulkCommandAndReturnsResults() { @@ -167,6 +174,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that UnAdviseItemBulkAsync propagates cancellation. + /// A task that represents the asynchronous operation. [Fact] public async Task UnAdviseItemBulkAsync_PropagatesCancellation() { @@ -180,6 +188,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that SubscribeBulkAsync surfaces per-entry failures. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeBulkAsync_SurfacesPerEntryFailures() { @@ -208,6 +217,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that SubscribeBulkAsync propagates cancellation. + /// A task that represents the asynchronous operation. [Fact] public async Task SubscribeBulkAsync_PropagatesCancellation() { @@ -221,6 +231,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that UnsubscribeBulkAsync forwards the command and returns results. + /// A task that represents the asynchronous operation. [Fact] public async Task UnsubscribeBulkAsync_ForwardsOneUnsubscribeBulkCommandAndReturnsResults() { @@ -246,6 +257,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that UnsubscribeBulkAsync propagates cancellation. + /// A task that represents the asynchronous operation. [Fact] public async Task UnsubscribeBulkAsync_PropagatesCancellation() { @@ -259,6 +271,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that WriteBulkAsync surfaces per-entry failures. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteBulkAsync_SurfacesPerEntryFailures() { @@ -292,6 +305,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that WriteBulkAsync propagates cancellation. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteBulkAsync_PropagatesCancellation() { @@ -308,6 +322,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that Write2BulkAsync forwards the command and preserves timestamp payload. + /// A task that represents the asynchronous operation. [Fact] public async Task Write2BulkAsync_ForwardsOneWrite2BulkCommandAndPreservesTimestampPayload() { @@ -351,6 +366,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that Write2BulkAsync propagates cancellation. + /// A task that represents the asynchronous operation. [Fact] public async Task Write2BulkAsync_PropagatesCancellation() { @@ -376,6 +392,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that WriteSecuredBulkAsync forwards the command and preserves credential payload. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteSecuredBulkAsync_ForwardsOneWriteSecuredBulkCommandAndPreservesCredentialPayload() { @@ -427,6 +444,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that WriteSecuredBulkAsync propagates cancellation. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteSecuredBulkAsync_PropagatesCancellation() { @@ -463,6 +481,7 @@ public sealed class SessionManagerBulkTests /// client closing its stream would hit, which the pre-cancel pattern can't /// exercise. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WriteSecuredBulkAsync_WhenCancelledMidFlight_ThrowsOperationCanceledForRequestToken() { @@ -499,6 +518,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that WriteSecured2BulkAsync forwards the command and preserves credential and timestamp payload. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteSecured2BulkAsync_ForwardsOneWriteSecured2BulkCommandAndPreservesCredentialAndTimestampPayload() { @@ -547,6 +567,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that WriteSecured2BulkAsync propagates cancellation. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteSecured2BulkAsync_PropagatesCancellation() { @@ -573,6 +594,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that ReadBulkAsync surfaces per-entry failures. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadBulkAsync_SurfacesPerEntryFailures() { @@ -619,6 +641,7 @@ public sealed class SessionManagerBulkTests } /// Verifies that ReadBulkAsync propagates cancellation. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadBulkAsync_PropagatesCancellation() { @@ -779,7 +802,8 @@ public sealed class SessionManagerBulkTests /// public void Kill(string reason) => State = WorkerClientState.Faulted; - /// + /// Releases resources held by the fake worker client. + /// A completed value task. public ValueTask DisposeAsync() => ValueTask.CompletedTask; } @@ -858,7 +882,8 @@ public sealed class SessionManagerBulkTests _invokeCompletion.TrySetCanceled(); } - /// + /// Cancels any pending invoke and releases resources held by this fake. + /// A completed value task. public ValueTask DisposeAsync() { _invokeCompletion.TrySetCanceled(); diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionManagerTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionManagerTests.cs index 45134bf..de76279 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionManagerTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionManagerTests.cs @@ -12,6 +12,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Sessions; public sealed class SessionManagerTests { /// Verifies that opening a session with a ready worker registers the session in ready state. + /// A task that represents the asynchronous operation. [Fact] public async Task OpenSessionAsync_WithWorkerReady_RegistersReadySession() { @@ -35,6 +36,7 @@ public sealed class SessionManagerTests } /// Verifies that opening a session sets the initial lease expiry from the configured default lease. + /// A task that represents the asynchronous operation. [Fact] public async Task OpenSessionAsync_SetsInitialDefaultLease() { @@ -51,6 +53,7 @@ public sealed class SessionManagerTests } /// Verifies that session generation creates client correlation ID from client name and session ID. + /// A task that represents the asynchronous operation. [Fact] public async Task OpenSessionAsync_GeneratesClientCorrelationIdFromClientNameAndSessionId() { @@ -67,6 +70,7 @@ public sealed class SessionManagerTests } /// Verifies that opening a session without a client session name uses the client correlation prefix. + /// A task that represents the asynchronous operation. [Fact] public async Task OpenSessionAsync_WhenClientSessionNameMissing_UsesClientCorrelationPrefix() { @@ -82,6 +86,7 @@ public sealed class SessionManagerTests } /// Verifies that invoking a command on a ready session forwards the command to the worker. + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_WhenSessionReady_ForwardsCommandToWorker() { @@ -99,6 +104,7 @@ public sealed class SessionManagerTests } /// Verifies that invoking a command on a ready session refreshes its lease expiry. + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_WhenSessionReady_RefreshesLease() { @@ -126,6 +132,7 @@ public sealed class SessionManagerTests } /// Verifies that gateway session subscribe bulk forwards one bulk command and returns results. + /// A task that represents the asynchronous operation. [Fact] public async Task GatewaySessionSubscribeBulkAsync_ForwardsOneBulkCommandAndReturnsResults() { @@ -171,6 +178,7 @@ public sealed class SessionManagerTests } /// Verifies that gateway session write bulk forwards one bulk command and returns results. + /// A task that represents the asynchronous operation. [Fact] public async Task GatewaySessionWriteBulkAsync_ForwardsOneBulkCommandAndReturnsResults() { @@ -236,6 +244,7 @@ public sealed class SessionManagerTests } /// Verifies that gateway session read bulk forwards one bulk command and returns results. + /// A task that represents the asynchronous operation. [Fact] public async Task GatewaySessionReadBulkAsync_ForwardsOneBulkCommandAndReturnsResults() { @@ -286,6 +295,7 @@ public sealed class SessionManagerTests } /// Verifies that invoking a command on a faulted session rejects the command. + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_WhenSessionFaulted_RejectsCommand() { @@ -311,6 +321,7 @@ public sealed class SessionManagerTests /// producing a self-contradictory "Session ... is not ready. Current /// state is Ready." message. /// + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_WhenWorkerNotReadyButSessionReady_DiagnosticIncludesBothStates() { @@ -335,6 +346,7 @@ public sealed class SessionManagerTests } /// Verifies that closing a session removes it from the registry. + /// A task that represents the asynchronous operation. [Fact] public async Task CloseSessionAsync_RemovesClosedSession() { @@ -356,6 +368,7 @@ public sealed class SessionManagerTests } /// Verifies that closing a session kills the worker when shutdown fails. + /// A task that represents the asynchronous operation. [Fact] public async Task CloseSessionAsync_WhenWorkerShutdownFails_KillsWorker() { @@ -377,6 +390,7 @@ public sealed class SessionManagerTests } /// Verifies that when worker shutdown fails, the session is removed and the slot is released. + /// A task that represents the asynchronous operation. [Fact] public async Task CloseSessionAsync_WhenWorkerShutdownFails_RemovesSessionAndReleasesSlot() { @@ -423,6 +437,7 @@ public sealed class SessionManagerTests } /// Verifies that when the second close is canceled, the session is not removed if owned by the first close. + /// A task that represents the asynchronous operation. [Fact] public async Task CloseSessionAsync_WhenSecondCloseIsCanceled_DoesNotRemoveSessionOwnedByFirstClose() { @@ -476,6 +491,7 @@ public sealed class SessionManagerTests /// SessionManager.KillWorkerAsyncsession.KillWorker(reason) → /// IWorkerClient.Kill(reason). /// + /// A task that represents the asynchronous operation. [Fact] public async Task KillWorkerAsync_KillsWorkerAndRemovesSession() { @@ -502,6 +518,7 @@ public sealed class SessionManagerTests /// before any session lookup or worker call runs. /// /// A blank or whitespace reason string. + /// A task that represents the asynchronous operation. [Theory] [InlineData("")] [InlineData(" ")] @@ -524,6 +541,7 @@ public sealed class SessionManagerTests /// with cannot carry null for a /// non-nullable string parameter on .NET 10, so the null case is its own fact. /// + /// A task that represents the asynchronous operation. [Fact] public async Task KillWorkerAsync_WithNullReason_ThrowsArgumentNullException() { @@ -539,6 +557,7 @@ public sealed class SessionManagerTests } /// Verifies that killing the worker for an unknown session raises SessionNotFound. + /// A task that represents the asynchronous operation. [Fact] public async Task KillWorkerAsync_WhenSessionMissing_ThrowsSessionNotFound() { @@ -555,6 +574,7 @@ public sealed class SessionManagerTests /// decrement mxgateway.sessions.open (parity with the Server-006 fix in /// OpenSessionAsync). Without the fix the gauge leaks one open session per failed kill. /// + /// A task that represents the asynchronous operation. [Fact] public async Task KillWorkerAsync_WhenSessionKillThrows_DecrementsOpenSessionGauge() { @@ -587,6 +607,7 @@ public sealed class SessionManagerTests /// double-increment mxgateway.sessions.closed. The first kill wins, the second /// observes wasClosed == true (or a missing session after removal) and short-circuits. /// + /// A task that represents the asynchronous operation. [Fact] public async Task KillWorkerAsync_ConcurrentCallsOnSameSession_CountClosedExactlyOnce() { @@ -627,6 +648,7 @@ public sealed class SessionManagerTests /// the mxgateway.sessions.closed counter must increment. Without the fix, the /// graceful-close failure path under-counts the closed counter. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ShutdownAsync_WhenSessionCloseThrows_StillDecrementsOpenSessionGaugeAndIncrementsClosedCounter() { @@ -655,6 +677,7 @@ public sealed class SessionManagerTests } /// Verifies that when worker creation fails, the session is removed from the registry. + /// A task that represents the asynchronous operation. [Fact] public async Task OpenSessionAsync_WhenWorkerCreationFails_RemovesSessionFromRegistry() { @@ -675,6 +698,7 @@ public sealed class SessionManagerTests } /// Verifies that closing expired leases only closes expired sessions. + /// A task that represents the asynchronous operation. [Fact] public async Task CloseExpiredLeasesAsync_ClosesExpiredSessionsOnly() { @@ -698,6 +722,7 @@ public sealed class SessionManagerTests } /// Verifies that an expired-lease sweep leaves a session with an active event subscriber open. + /// A task that represents the asynchronous operation. [Fact] public async Task CloseExpiredLeasesAsync_DoesNotCloseActiveEventSubscriber() { @@ -716,6 +741,7 @@ public sealed class SessionManagerTests } /// Verifies that shutdown closes all registered sessions. + /// A task that represents the asynchronous operation. [Fact] public async Task ShutdownAsync_ClosesAllRegisteredSessions() { @@ -859,16 +885,16 @@ public sealed class SessionManagerTests private sealed class FakeWorkerClient : IWorkerClient { - /// Gets the session ID for the fake worker client. + /// public string SessionId { get; init; } = "session-1"; - /// Gets the process ID for the fake worker client. + /// public int? ProcessId { get; init; } = 1234; - /// Gets or sets the state of the fake worker client. + /// public WorkerClientState State { get; set; } = WorkerClientState.Ready; - /// Gets the last heartbeat timestamp for the fake worker client. + /// public DateTimeOffset LastHeartbeatAt { get; init; } = DateTimeOffset.UtcNow; /// Gets the number of times invoke was called on the fake worker client. @@ -984,7 +1010,8 @@ public sealed class SessionManagerTests State = WorkerClientState.Faulted; } - /// + /// Increments the dispose count and returns a completed task. + /// A completed value task. public ValueTask DisposeAsync() { DisposeCount++; @@ -992,6 +1019,7 @@ public sealed class SessionManagerTests } /// Waits for shutdown to start on the fake worker client. + /// A task that resolves once shutdown has started. public Task WaitForShutdownStartAsync() { return ShutdownStarted.Task.WaitAsync(TimeSpan.FromSeconds(5)); diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionWorkerClientFactoryFakeWorkerTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionWorkerClientFactoryFakeWorkerTests.cs index a61cb96..1f76d14 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionWorkerClientFactoryFakeWorkerTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Sessions/SessionWorkerClientFactoryFakeWorkerTests.cs @@ -20,6 +20,7 @@ public sealed class SessionWorkerClientFactoryFakeWorkerTests : IAsyncDisposable /// Awaits every scripted worker task so an unhandled exception fails the owning test /// instead of surfacing later as an unobserved . /// + /// A task that represents the asynchronous dispose operation. public async ValueTask DisposeAsync() { foreach (IWorkerTaskLauncher launcher in _launchers) @@ -29,6 +30,7 @@ public sealed class SessionWorkerClientFactoryFakeWorkerTests : IAsyncDisposable } /// Verifies that the factory creates a ready worker client with a scripted fake worker process. + /// A task that represents the asynchronous operation. [Fact] public async Task CreateAsync_WithScriptedFakeWorker_ReturnsReadyClient() { @@ -62,6 +64,7 @@ public sealed class SessionWorkerClientFactoryFakeWorkerTests : IAsyncDisposable } /// Verifies that a failed fake worker startup throws a worker client exception. + /// A task that represents the asynchronous operation. [Fact] public async Task CreateAsync_WhenFakeWorkerStartupFails_ThrowsWorkerClientException() { @@ -82,6 +85,7 @@ public sealed class SessionWorkerClientFactoryFakeWorkerTests : IAsyncDisposable } /// Verifies that a worker that never sends ready times out and is killed. + /// A task that represents the asynchronous operation. [Fact] public async Task CreateAsync_WhenFakeWorkerNeverSendsReady_TimesOutAndKillsWorker() { @@ -167,6 +171,7 @@ public sealed class SessionWorkerClientFactoryFakeWorkerTests : IAsyncDisposable /// teardown faults expected when the worker client kills or disposes the worker. /// /// Maximum time to wait for the worker task. + /// A task that represents the asynchronous operation. Task ObserveWorkerTaskAsync(TimeSpan timeout); } @@ -348,10 +353,10 @@ public sealed class SessionWorkerClientFactoryFakeWorkerTests : IAsyncDisposable /// public int Id { get; } = processId; - /// Gets a value indicating whether the process has exited. + /// public bool HasExited { get; private set; } - /// Gets the process exit code, or null if the process has not exited. + /// public int? ExitCode { get; private set; } /// Gets the number of times the Kill method was called. @@ -370,7 +375,7 @@ public sealed class SessionWorkerClientFactoryFakeWorkerTests : IAsyncDisposable MarkExited(-1); } - /// + /// Marks this fake process as disposed. public void Dispose() { _disposed = true; diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/FakeWorkerHarnessTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/FakeWorkerHarnessTests.cs index a4edd57..020c231 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/FakeWorkerHarnessTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/FakeWorkerHarnessTests.cs @@ -11,6 +11,7 @@ public sealed class FakeWorkerHarnessTests private static readonly TimeSpan TestTimeout = TimeSpan.FromSeconds(5); /// Verifies that completing startup with hello and ready transitions the client to ready state. + /// A task that represents the asynchronous operation. [Fact] public async Task CompleteStartupAsync_WithHelloAndReady_TransitionsClientToReady() { @@ -28,6 +29,7 @@ public sealed class FakeWorkerHarnessTests } /// Verifies that a protocol version mismatch during startup fails the client. + /// A task that represents the asynchronous operation. [Fact] public async Task StartAsync_WithProtocolMismatch_FailsStartup() { @@ -47,6 +49,7 @@ public sealed class FakeWorkerHarnessTests } /// Verifies that a scripted reply completes a pending command invocation. + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_WithScriptedReply_CompletesCommand() { @@ -69,6 +72,7 @@ public sealed class FakeWorkerHarnessTests } /// Verifies that scripted events are yielded in order through the event stream. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadEventsAsync_WithScriptedEvents_YieldsOrderedEvents() { @@ -93,6 +97,7 @@ public sealed class FakeWorkerHarnessTests } /// Verifies that a scripted fault from the worker faults the client. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadLoop_WithScriptedFault_FaultsClient() { @@ -116,6 +121,7 @@ public sealed class FakeWorkerHarnessTests /// so the timestamp advance is deterministic rather /// than relying on a wall-clock Task.Delay exceeding clock resolution. /// + /// A task that represents the asynchronous operation. [Fact] public async Task SendHeartbeatAsync_UpdatesClientHeartbeatState() { @@ -138,6 +144,7 @@ public sealed class FakeWorkerHarnessTests } /// Verifies that a hung worker times out pending command invocations. + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_WithHungWorker_TimesOutPendingCommand() { @@ -159,6 +166,7 @@ public sealed class FakeWorkerHarnessTests } /// Verifies that a malformed frame in the read loop faults the client. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadLoop_WithMalformedFrame_FaultsClient() { @@ -176,6 +184,7 @@ public sealed class FakeWorkerHarnessTests } /// Verifies that a shutdown acknowledgment from the worker closes the client. + /// A task that represents the asynchronous operation. [Fact] public async Task ShutdownAsync_WithShutdownAck_ClosesClient() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/Fakes/FakeWorkerHarness.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/Fakes/FakeWorkerHarness.cs index 9d03b6d..52f8802 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/Fakes/FakeWorkerHarness.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/Fakes/FakeWorkerHarness.cs @@ -52,6 +52,7 @@ public sealed class FakeWorkerHarness : IAsyncDisposable /// Protocol version for frame communication. /// Maximum message size in bytes. /// Token to cancel the asynchronous operation. + /// A task that resolves to a connected instance. public static async Task CreateConnectedPairAsync( string sessionId = DefaultSessionId, string nonce = DefaultNonce, @@ -87,6 +88,7 @@ public sealed class FakeWorkerHarness : IAsyncDisposable /// Protocol version for frame communication. /// Maximum message size in bytes. /// Token to cancel the asynchronous operation. + /// A task that resolves to a connected instance. public static async Task ConnectToGatewayPipeAsync( string sessionId, string nonce, @@ -205,6 +207,7 @@ public sealed class FakeWorkerHarness : IAsyncDisposable /// Protocol version override. /// Nonce override. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. public async Task SendWorkerHelloAsync( int workerProcessId = DefaultWorkerProcessId, string workerVersion = "fake-worker", @@ -230,6 +233,7 @@ public sealed class FakeWorkerHarness : IAsyncDisposable /// MXAccess COM ProgID. /// MXAccess COM CLSID. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. public async Task SendWorkerReadyAsync( int workerProcessId = DefaultWorkerProcessId, string mxaccessProgid = "LMXProxy.LMXProxyServer.1", @@ -255,6 +259,7 @@ public sealed class FakeWorkerHarness : IAsyncDisposable /// Human-readable status message. /// Optional callback to customize the reply. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. public async Task ReplyToCommandAsync( WorkerEnvelope commandEnvelope, ProtocolStatusCode statusCode = ProtocolStatusCode.Ok, @@ -296,6 +301,7 @@ public sealed class FakeWorkerHarness : IAsyncDisposable /// Family of the event to emit. /// Token to cancel the asynchronous operation. /// Optional callback to customize the event. + /// A task that represents the asynchronous operation. public async Task EmitEventAsync( MxEventFamily family, CancellationToken cancellationToken = default, @@ -325,6 +331,7 @@ public sealed class FakeWorkerHarness : IAsyncDisposable /// Category of the fault. /// Diagnostic message describing the fault. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. public async Task EmitFaultAsync( WorkerFaultCategory category, string diagnosticMessage, @@ -350,6 +357,7 @@ public sealed class FakeWorkerHarness : IAsyncDisposable /// Current worker state. /// Token to cancel the asynchronous operation. /// Optional callback to customize the heartbeat. + /// A task that represents the asynchronous operation. public async Task SendHeartbeatAsync( WorkerState state = WorkerState.Ready, CancellationToken cancellationToken = default, @@ -373,6 +381,7 @@ public sealed class FakeWorkerHarness : IAsyncDisposable /// Sends a shutdown acknowledgment message to the gateway. /// Protocol status code for the acknowledgment. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. public async Task SendShutdownAckAsync( ProtocolStatusCode statusCode = ProtocolStatusCode.Ok, CancellationToken cancellationToken = default) @@ -394,6 +403,7 @@ public sealed class FakeWorkerHarness : IAsyncDisposable /// Writes a malformed payload directly to the worker stream. /// Malformed payload bytes to write. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. public async Task WriteMalformedPayloadAsync( ReadOnlyMemory payload, CancellationToken cancellationToken = default) @@ -412,6 +422,7 @@ public sealed class FakeWorkerHarness : IAsyncDisposable /// Writes an oversized frame header to the worker stream for testing frame size limits. /// Length of the oversized payload in bytes. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. public async Task WriteOversizedFrameHeaderAsync( uint payloadLength, CancellationToken cancellationToken = default) @@ -430,6 +441,7 @@ public sealed class FakeWorkerHarness : IAsyncDisposable } /// Disposes the worker-side stream. + /// A value task that represents the asynchronous operation. public async ValueTask DisposeWorkerSideAsync() { if (_workerSideDisposed) @@ -441,7 +453,8 @@ public sealed class FakeWorkerHarness : IAsyncDisposable _workerSideDisposed = true; } - /// + /// Disposes both the worker-side and gateway-side streams. + /// A value task that represents the asynchronous operation. public async ValueTask DisposeAsync() { await DisposeWorkerSideAsync().ConfigureAwait(false); diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/OrphanWorkerTerminatorTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/OrphanWorkerTerminatorTests.cs index 3f407a7..8670b37 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/OrphanWorkerTerminatorTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/OrphanWorkerTerminatorTests.cs @@ -129,12 +129,10 @@ public sealed class OrphanWorkerTerminatorTests /// Gets or sets the process ID that should throw when killed. public int? ThrowOnKillProcessId { get; init; } - /// Gets the list of running processes by name. - /// The process name to search for. + /// public IReadOnlyList GetProcessesByName(string processName) => processes; - /// Kills the specified process or records the kill attempt. - /// The process identifier to kill. + /// public void Kill(int processId) { if (ThrowOnKillProcessId == processId) diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerClientTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerClientTests.cs index 7d46680..da10949 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerClientTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerClientTests.cs @@ -16,6 +16,7 @@ public sealed class WorkerClientTests private static readonly TimeSpan TestTimeout = TimeSpan.FromSeconds(5); /// Verifies that StartAsync enters ready state after receiving worker hello and ready messages. + /// A task that represents the asynchronous operation. [Fact] public async Task StartAsync_WithWorkerHelloAndReady_EntersReadyState() { @@ -29,6 +30,7 @@ public sealed class WorkerClientTests } /// Verifies that InvokeAsync completes a pending command when a matching reply arrives. + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_WithMatchingReply_CompletesPendingCommand() { @@ -55,6 +57,7 @@ public sealed class WorkerClientTests } /// Verifies that InvokeAsync ignores late replies and keeps the client ready. + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_WithLateReply_IgnoresLateReplyAndKeepsClientReady() { @@ -93,6 +96,7 @@ public sealed class WorkerClientTests } /// Verifies that ReadEventsAsync yields events in pipe order from the worker. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadEventsAsync_WithWorkerEvents_YieldsEventsInPipeOrder() { @@ -119,6 +123,7 @@ public sealed class WorkerClientTests } /// Verifies that the read loop faults the client when the event queue overflows. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadLoop_WhenEventQueueOverflows_FaultsClient() { @@ -154,6 +159,7 @@ public sealed class WorkerClientTests /// Faulted state before it calls KillOwnedProcess, so a state-based /// wait can observe Faulted while KillCount is still 0. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ReadLoop_WhenClientFaults_KillsOwnedWorkerProcess() { @@ -192,6 +198,7 @@ public sealed class WorkerClientTests /// invoke task with a carrying the /// pipe-disconnected error code rather than hanging until the command timeout. /// + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_WhenPipeDisconnectsMidCommand_FailsPendingInvokeWithPipeDisconnected() { @@ -223,6 +230,7 @@ public sealed class WorkerClientTests /// task with a carrying the worker-faulted /// error code. /// + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_WhenWorkerFaultsMidCommand_FailsPendingInvokeWithWorkerFaulted() { @@ -248,6 +256,7 @@ public sealed class WorkerClientTests } /// Verifies that pipe disconnect faults the client. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadLoop_WhenPipeDisconnects_FaultsClient() { @@ -265,6 +274,7 @@ public sealed class WorkerClientTests } /// Verifies that the read loop stops the running worker metric when the pipe disconnects. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadLoop_WhenPipeDisconnects_StopsRunningWorkerMetric() { @@ -288,6 +298,7 @@ public sealed class WorkerClientTests } /// Verifies that DisposeAsync returns within a bounded timeout when the pipe read is blocked. + /// A task that represents the asynchronous operation. [Fact] public async Task DisposeAsync_WhenPipeReadIsBlocked_ReturnsWithinBoundedTimeout() { @@ -304,7 +315,8 @@ public sealed class WorkerClientTests $"DisposeAsync took {elapsed.TotalMilliseconds:N0}ms."); } - /// Verifies that the read loop updates the last heartbeat and worker process when a heartbeat arrives. + /// Verifies that DisposeAsync kills the owned worker process before disposing. + /// A task that represents the asynchronous operation. [Fact] public async Task DisposeAsync_WhenOwnedWorkerStillRuns_KillsProcessBeforeDisposing() { @@ -325,6 +337,7 @@ public sealed class WorkerClientTests /// deterministic instead of relying on a wall-clock Task.Delay exceeding /// resolution. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ReadLoop_WhenHeartbeatArrives_UpdatesLastHeartbeatAndWorkerProcess() { @@ -352,6 +365,7 @@ public sealed class WorkerClientTests /// timer stays on the real clock and /// observes the manually-advanced grace on its next tick. /// + /// A task that represents the asynchronous operation. [Fact] public async Task HeartbeatMonitor_WhenHeartbeatExpires_FaultsClient() { @@ -385,6 +399,7 @@ public sealed class WorkerClientTests /// gap is more likely caused by pipe-write contention than by a hung /// worker. Mirrors Worker-023 on the worker side. /// + /// A task that represents the asynchronous operation. [Fact] public async Task HeartbeatMonitor_WhenCommandInFlightWithinCeiling_DoesNotFaultOnExpiredHeartbeat() { @@ -427,6 +442,7 @@ public sealed class WorkerClientTests /// heartbeat watchdog fires anyway — a truly stuck COM call shouldn't /// keep the watchdog suppressed indefinitely. /// + /// A task that represents the asynchronous operation. [Fact] public async Task HeartbeatMonitor_WhenPendingCommandExceedsStuckCeiling_FaultsClient() { @@ -471,6 +487,7 @@ public sealed class WorkerClientTests /// and the diagnostic must name the channel capacity, depth, and /// actionable remediation. /// + /// A task that represents the asynchronous operation. [Fact] public async Task EnqueueWorkerEvent_WhenChannelFullPastTimeout_FaultsWithRichDiagnostic() { @@ -722,6 +739,7 @@ public sealed class WorkerClientTests public WorkerFrameWriter WorkerWriter { get; } /// Creates a connected pipe pair for testing. + /// A task that resolves to the connected pipe pair. public static async Task CreateAsync() { string pipeName = $"mxaccessgw-workerclient-tests-{Guid.NewGuid():N}"; @@ -745,6 +763,7 @@ public sealed class WorkerClientTests } /// Disposes the worker side of the pipe. + /// A task that represents the asynchronous operation. public async ValueTask DisposeWorkerSideAsync() { if (_workerSideDisposed) @@ -757,6 +776,7 @@ public sealed class WorkerClientTests } /// Disposes the duplex stream. + /// A task that represents the asynchronous operation. public async ValueTask DisposeAsync() { await DisposeWorkerSideAsync(); @@ -768,13 +788,13 @@ public sealed class WorkerClientTests { private readonly TaskCompletionSource _exited = new(TaskCreationOptions.RunContinuationsAsynchronously); - /// Gets the process ID. + /// public int Id { get; } = WorkerProcessId; - /// Gets a value indicating whether the process has exited. + /// public bool HasExited { get; private set; } - /// Gets the process exit code. + /// public int? ExitCode { get; private set; } /// Gets the number of times kill was called. @@ -792,8 +812,7 @@ public sealed class WorkerClientTests return new ValueTask(_exited.Task.WaitAsync(cancellationToken)); } - /// Records a kill request. - /// Whether to kill the entire process tree. + /// public void Kill(bool entireProcessTree) { KillCount++; @@ -803,7 +822,7 @@ public sealed class WorkerClientTests _exited.TrySetResult(); } - /// + /// Marks the process as disposed. public void Dispose() { Disposed = true; diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerExecutableValidatorTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerExecutableValidatorTests.cs index 6f94e73..29f816d 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerExecutableValidatorTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerExecutableValidatorTests.cs @@ -129,7 +129,7 @@ public sealed class WorkerExecutableValidatorTests : IDisposable return path; } - /// + /// Deletes all temporary PE fixture files created during the test. public void Dispose() { foreach (string path in _tempFiles) diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerFrameProtocolTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerFrameProtocolTests.cs index 6696ac6..ed26f6d 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerFrameProtocolTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerFrameProtocolTests.cs @@ -11,6 +11,7 @@ public sealed class WorkerFrameProtocolTests private const string SessionId = "session-1"; /// Verifies that writing and reading a valid envelope round-trips the frame correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAndReadAsync_WithValidEnvelope_RoundTripsFrame() { @@ -29,6 +30,7 @@ public sealed class WorkerFrameProtocolTests } /// Verifies that reading a frame with partial reads reassembles the frame correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_WithPartialReads_ReassemblesFrame() { @@ -45,6 +47,7 @@ public sealed class WorkerFrameProtocolTests } /// Verifies that reading a frame with zero length throws a malformed length exception. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_WithZeroLengthFrame_ThrowsMalformedLength() { @@ -60,6 +63,7 @@ public sealed class WorkerFrameProtocolTests } /// Verifies that reading a frame with oversized length throws before allocating the payload. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_WithOversizedLength_ThrowsBeforePayloadAllocation() { @@ -77,6 +81,7 @@ public sealed class WorkerFrameProtocolTests } /// Verifies that reading a frame with wrong protocol version throws a protocol version mismatch exception. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_WithWrongProtocolVersion_ThrowsProtocolVersionMismatch() { @@ -94,6 +99,7 @@ public sealed class WorkerFrameProtocolTests } /// Verifies that reading a frame with wrong session ID throws a session mismatch exception. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_WithWrongSessionId_ThrowsSessionMismatch() { @@ -111,6 +117,7 @@ public sealed class WorkerFrameProtocolTests } /// Verifies that reading a frame with malformed payload throws an invalid envelope exception. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_WithMalformedPayload_ThrowsInvalidEnvelope() { @@ -127,6 +134,7 @@ public sealed class WorkerFrameProtocolTests } /// Verifies that reading a frame with missing envelope body throws an invalid envelope exception. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_WithMissingEnvelopeBody_ThrowsInvalidEnvelope() { @@ -144,6 +152,7 @@ public sealed class WorkerFrameProtocolTests } /// Verifies that writing an oversized envelope throws a message too large exception. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAsync_WithOversizedEnvelope_ThrowsMessageTooLarge() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerProcessLauncherTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerProcessLauncherTests.cs index f8f93a5..5fd6bac 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerProcessLauncherTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Gateway/Workers/WorkerProcessLauncherTests.cs @@ -14,6 +14,7 @@ public sealed class WorkerProcessLauncherTests private const string Nonce = "super-secret-nonce"; /// Verifies that a valid worker executable starts with correct bootstrap arguments and nonce environment variable. + /// A task that represents the asynchronous operation. [Fact] public async Task LaunchAsync_WithValidWorker_StartsProcessWithBootstrapArgumentsAndNonceEnvironment() { @@ -48,6 +49,7 @@ public sealed class WorkerProcessLauncherTests } /// Verifies that a failed startup probe kills and disposes the worker process. + /// A task that represents the asynchronous operation. [Fact] public async Task LaunchAsync_WhenStartupProbeFails_KillsAndDisposesWorker() { @@ -74,6 +76,7 @@ public sealed class WorkerProcessLauncherTests } /// Verifies that transient startup probe failures are retried without respawning the worker process. + /// A task that represents the asynchronous operation. [Fact] public async Task LaunchAsync_WhenStartupProbeFailsTransiently_RetriesWithoutRespawningWorker() { @@ -101,6 +104,7 @@ public sealed class WorkerProcessLauncherTests } /// Verifies that a startup probe timeout kills and disposes the worker process. + /// A task that represents the asynchronous operation. [Fact] public async Task LaunchAsync_WhenStartupTimesOut_KillsAndDisposesWorker() { @@ -126,6 +130,7 @@ public sealed class WorkerProcessLauncherTests } /// Verifies that a missing worker executable fails before attempting to start the process. + /// A task that represents the asynchronous operation. [Fact] public async Task LaunchAsync_WhenExecutableDoesNotExist_FailsBeforeStartingProcess() { @@ -143,6 +148,7 @@ public sealed class WorkerProcessLauncherTests } /// Verifies that a worker executable with mismatched architecture fails before attempting to start. + /// A task that represents the asynchronous operation. [Fact] public async Task LaunchAsync_WhenExecutableArchitectureDoesNotMatch_FailsBeforeStartingProcess() { @@ -160,6 +166,7 @@ public sealed class WorkerProcessLauncherTests } /// Verifies that a worker that has already exited fails and disposes without additional killing. + /// A task that represents the asynchronous operation. [Fact] public async Task LaunchAsync_WhenWorkerAlreadyExited_FailsAndDisposesWorkerWithoutKill() { @@ -246,10 +253,10 @@ public sealed class WorkerProcessLauncherTests /// public int Id { get; } = processId; - /// Gets or sets a value indicating whether the process has exited. + /// public bool HasExited { get; set; } - /// Gets or sets the process exit code. + /// public int? ExitCode { get; set; } /// Gets a value indicating whether the Dispose method was called. @@ -272,7 +279,7 @@ public sealed class WorkerProcessLauncherTests HasExited = true; } - /// + /// Records that the fake process was disposed. public void Dispose() { DisposeCalled = true; @@ -344,7 +351,7 @@ public sealed class WorkerProcessLauncherTests /// Gets a value indicating whether the Dispose method was called. public bool DisposeCalled { get; private set; } - /// + /// Records that the pipe reservation was disposed. public void Dispose() { DisposeCalled = true; @@ -363,6 +370,7 @@ public sealed class WorkerProcessLauncherTests public string Path { get; } /// Creates a new temporary directory for testing. + /// A new backed by a freshly created temp directory. public static TestDirectory Create() { string path = System.IO.Path.Combine(System.IO.Path.GetTempPath(), $"mxgateway-tests-{Guid.NewGuid():N}"); @@ -391,7 +399,7 @@ public sealed class WorkerProcessLauncherTests return path; } - /// + /// Deletes the temporary directory and all its contents. public void Dispose() { Directory.Delete(Path, recursive: true); diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Security/Audit/CanonicalAuditStoreAndAdapterTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Security/Audit/CanonicalAuditStoreAndAdapterTests.cs index 2eefb63..240e50c 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Security/Audit/CanonicalAuditStoreAndAdapterTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Security/Audit/CanonicalAuditStoreAndAdapterTests.cs @@ -21,6 +21,7 @@ public sealed class CanonicalAuditStoreAndAdapterTests : IDisposable private readonly List _tempDirectories = []; /// A canonical event with all fields populated round-trips through the store. + /// A task that represents the asynchronous operation. [Fact] public async Task Store_InsertThenListRecent_RoundTripsAllFields() { @@ -61,6 +62,7 @@ public sealed class CanonicalAuditStoreAndAdapterTests : IDisposable } /// Nullable canonical fields round-trip as null, not empty string. + /// A task that represents the asynchronous operation. [Fact] public async Task Store_InsertWithNullOptionalFields_RoundTripsAsNull() { @@ -86,6 +88,7 @@ public sealed class CanonicalAuditStoreAndAdapterTests : IDisposable } /// ListRecent returns newest-first. + /// A task that represents the asynchronous operation. [Fact] public async Task Store_ListRecent_ReturnsNewestFirst() { @@ -100,6 +103,7 @@ public sealed class CanonicalAuditStoreAndAdapterTests : IDisposable } /// The writer is best-effort: a faulting store does not surface to the caller. + /// A task that represents the asynchronous operation. [Fact] public async Task Writer_WhenStoreFails_DoesNotThrow() { @@ -123,6 +127,7 @@ public sealed class CanonicalAuditStoreAndAdapterTests : IDisposable /// A library event with a KeyId maps to a canonical Success event under category ApiKey, /// and the adapter maps it back to the original entry for the dashboard view. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Adapter_KeyedEvent_RoundTripsThroughCanonicalStore() { @@ -158,6 +163,7 @@ public sealed class CanonicalAuditStoreAndAdapterTests : IDisposable } /// The keyless library init-db event maps to Actor "system" and back to a null KeyId. + /// A task that represents the asynchronous operation. [Fact] public async Task Adapter_InitDbKeylessEvent_MapsToSystemActor() { @@ -184,6 +190,7 @@ public sealed class CanonicalAuditStoreAndAdapterTests : IDisposable } /// Any other keyless library event maps to Actor "cli" and back to a null KeyId. + /// A task that represents the asynchronous operation. [Fact] public async Task Adapter_OtherKeylessEvent_MapsToCliActor() { @@ -207,6 +214,7 @@ public sealed class CanonicalAuditStoreAndAdapterTests : IDisposable } /// A constraint-denied library event maps to Outcome.Denied. + /// A task that represents the asynchronous operation. [Fact] public async Task Adapter_ConstraintDeniedEvent_MapsToDeniedOutcome() { @@ -234,6 +242,7 @@ public sealed class CanonicalAuditStoreAndAdapterTests : IDisposable /// The adapter does NOT throw when the underlying write fails (it forwards through the /// best-effort writer), preserving the IApiKeyAuditStore caller's flow. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Adapter_WhenWriterFails_DoesNotThrow() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/ApiKeyAdminCliRunnerTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/ApiKeyAdminCliRunnerTests.cs index c6cce0f..e95d5cc 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/ApiKeyAdminCliRunnerTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/ApiKeyAdminCliRunnerTests.cs @@ -14,6 +14,7 @@ public sealed class ApiKeyAdminCliRunnerTests : IDisposable { private readonly List _tempDirectories = []; /// Verifies that CreateKeyAsync creates an authenticating key and audits the action. + /// A task that represents the asynchronous operation. [Fact] public async Task CreateKeyAsync_CreatesAuthenticatingKeyAndAudits() { @@ -52,6 +53,7 @@ public sealed class ApiKeyAdminCliRunnerTests : IDisposable } /// Verifies that ListKeysAsync does not print the raw secret. + /// A task that represents the asynchronous operation. [Fact] public async Task ListKeysAsync_DoesNotPrintRawSecret() { @@ -82,6 +84,7 @@ public sealed class ApiKeyAdminCliRunnerTests : IDisposable } /// Verifies that RevokeKeyAsync causes the revoked key to fail verification and is audited. + /// A task that represents the asynchronous operation. [Fact] public async Task RevokeKeyAsync_RevokedKeyFailsVerificationAndAudits() { @@ -117,6 +120,7 @@ public sealed class ApiKeyAdminCliRunnerTests : IDisposable } /// Verifies that RotateKeyAsync prints the new secret once and invalidates the old secret. + /// A task that represents the asynchronous operation. [Fact] public async Task RotateKeyAsync_PrintsNewSecretOnceAndInvalidatesOldSecret() { @@ -154,6 +158,7 @@ public sealed class ApiKeyAdminCliRunnerTests : IDisposable } /// Verifies that CreateKeyAsync prints the raw secret exactly once. + /// A task that represents the asynchronous operation. [Fact] public async Task CreateKeyAsync_PrintsRawSecretExactlyOnce() { @@ -182,6 +187,7 @@ public sealed class ApiKeyAdminCliRunnerTests : IDisposable } /// Verifies that API key constraints are persisted correctly. + /// A task that represents the asynchronous operation. [Fact] public async Task CreateKeyAsync_WithConstraints_PersistsConstraints() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/ApiKeyVerifierTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/ApiKeyVerifierTests.cs index 5fa6f0e..6d8f8c5 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/ApiKeyVerifierTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/ApiKeyVerifierTests.cs @@ -18,6 +18,7 @@ public sealed class ApiKeyVerifierTests private static readonly ApiKeyOptions Options = new() { TokenPrefix = "mxgw" }; /// Verifies that VerifyAsync returns identity and scopes for a valid key. + /// A task that represents the asynchronous operation. [Fact] public async Task VerifyAsync_ValidKey_ReturnsIdentityAndScopes() { @@ -38,6 +39,7 @@ public sealed class ApiKeyVerifierTests } /// Verifies that VerifyAsync does not expose the raw secret in the result. + /// A task that represents the asynchronous operation. [Fact] public async Task VerifyAsync_ValidKey_DoesNotExposeRawSecretInResult() { @@ -55,6 +57,7 @@ public sealed class ApiKeyVerifierTests /// Verifies that VerifyAsync fails as missing/malformed for a malformed key. /// Authorization header value to test. + /// A task that represents the asynchronous operation. [Theory] [InlineData("")] [InlineData("Bearer mxgw_operator01")] @@ -72,6 +75,7 @@ public sealed class ApiKeyVerifierTests } /// Verifies that VerifyAsync fails for an unknown key. + /// A task that represents the asynchronous operation. [Fact] public async Task VerifyAsync_UnknownKey_Fails() { @@ -86,6 +90,7 @@ public sealed class ApiKeyVerifierTests } /// Verifies that VerifyAsync fails for a wrong secret (constant-time compare rejects it). + /// A task that represents the asynchronous operation. [Fact] public async Task VerifyAsync_WrongSecret_Fails() { @@ -102,6 +107,7 @@ public sealed class ApiKeyVerifierTests } /// Verifies that VerifyAsync fails for a revoked key. + /// A task that represents the asynchronous operation. [Fact] public async Task VerifyAsync_RevokedKey_Fails() { @@ -118,6 +124,7 @@ public sealed class ApiKeyVerifierTests } /// Verifies that VerifyAsync fails closed when the pepper is missing. + /// A task that represents the asynchronous operation. [Fact] public async Task VerifyAsync_MissingPepper_Fails() { @@ -160,6 +167,7 @@ public sealed class ApiKeyVerifierTests private sealed class FakePepperProvider(string? pepper) : IApiKeyPepperProvider { /// Returns the configured pepper (or null to simulate an unavailable pepper). + /// The pepper string, or null if unavailable. public string? GetPepper() => pepper; } @@ -169,13 +177,19 @@ public sealed class ApiKeyVerifierTests /// Gets whether the key was marked as used. public bool MarkedUsed { get; private set; } - /// + /// Returns the stored key record if the key ID matches; otherwise null. + /// The key ID to look up. + /// Cancellation token. + /// A task that resolves to the matching key record, or null if not found. public Task FindByKeyIdAsync(string keyId, CancellationToken ct) { return Task.FromResult(storedKey?.KeyId == keyId ? storedKey : null); } - /// + /// Returns the stored key record if the key ID matches and the key is not revoked; otherwise null. + /// The key ID to look up. + /// Cancellation token. + /// A task that resolves to the active key record, or null if not found or revoked. public Task FindActiveByKeyIdAsync(string keyId, CancellationToken ct) { return Task.FromResult( @@ -184,7 +198,11 @@ public sealed class ApiKeyVerifierTests : null); } - /// + /// Records that the key was used. + /// The key ID to mark as used. + /// The timestamp of use. + /// Cancellation token. + /// A task that represents the asynchronous operation. public Task MarkUsedAsync(string keyId, DateTimeOffset whenUtc, CancellationToken ct) { MarkedUsed = storedKey?.KeyId == keyId; diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/SqliteAuthStoreTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/SqliteAuthStoreTests.cs index 8b6b633..9e7fb7b 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/SqliteAuthStoreTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/SqliteAuthStoreTests.cs @@ -23,6 +23,7 @@ public sealed class SqliteAuthStoreTests : IDisposable /// /// Verifies that MigrateAsync initializes the database schema at the donor's version (2). /// + /// A task that represents the asynchronous operation. [Fact] public async Task MigrateAsync_EmptyDatabase_InitializesCurrentSchema() { @@ -41,6 +42,7 @@ public sealed class SqliteAuthStoreTests : IDisposable /// /// Verifies that MigrateAsync migrates and is idempotent. /// + /// A task that represents the asynchronous operation. [Fact] public async Task MigrateAsync_ExistingVersionZeroDatabase_MigratesIdempotently() { @@ -61,6 +63,7 @@ public sealed class SqliteAuthStoreTests : IDisposable /// /// Verifies that gateway startup fails with a newer schema version. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StartAsync_NewerSchemaVersion_BlocksStartup() { @@ -83,6 +86,7 @@ public sealed class SqliteAuthStoreTests : IDisposable /// Verifies that FindActiveByKeyIdAsync returns an active key, reading a row whose columns match /// the donor schema (peppered secret_hash BLOB, ordinal-sorted scopes JSON). /// + /// A task that represents the asynchronous operation. [Fact] public async Task FindActiveByKeyIdAsync_ExistingActiveKey_ReturnsKey() { @@ -106,6 +110,7 @@ public sealed class SqliteAuthStoreTests : IDisposable /// /// Verifies that FindActiveByKeyIdAsync returns null for a revoked key. /// + /// A task that represents the asynchronous operation. [Fact] public async Task FindActiveByKeyIdAsync_RevokedKey_ReturnsNull() { @@ -129,6 +134,7 @@ public sealed class SqliteAuthStoreTests : IDisposable /// /// Verifies that the audit store persists audit events. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ApiKeyAuditStore_AppendAsync_PersistsAuditEvent() { @@ -163,6 +169,7 @@ public sealed class SqliteAuthStoreTests : IDisposable /// the auth database in WAL journal mode so concurrent readers and writers degrade /// gracefully instead of surfacing SQLITE_BUSY on the request path. /// + /// A task that represents the asynchronous operation. [Fact] public async Task OpenConnectionAsync_EnablesWalJournalModeAndBusyTimeout() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/TempDatabaseDirectory.cs b/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/TempDatabaseDirectory.cs index ff97cab..96cff32 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/TempDatabaseDirectory.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Security/Authentication/TempDatabaseDirectory.cs @@ -22,6 +22,7 @@ internal sealed class TempDatabaseDirectory : IDisposable /// Creates a new uniquely named temporary directory under the given prefix. /// Folder name placed under %TEMP% to group related test directories. + /// A new wrapping the created path. public static TempDatabaseDirectory Create(string prefix) { string path = System.IO.Path.Combine( @@ -35,12 +36,13 @@ internal sealed class TempDatabaseDirectory : IDisposable /// Returns a database file path inside this temporary directory. /// Database file name; defaults to the gateway auth database name. + /// The full path to the named file inside this directory. public string DatabasePath(string fileName = "gateway-auth.db") { return System.IO.Path.Combine(Path, fileName); } - /// + /// Clears SQLite connection pools and deletes the temporary directory. public void Dispose() { if (_disposed) diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Security/Authorization/ConstraintEnforcerTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Security/Authorization/ConstraintEnforcerTests.cs index 379ce2d..7982525 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Security/Authorization/ConstraintEnforcerTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Security/Authorization/ConstraintEnforcerTests.cs @@ -16,6 +16,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Security.Authorization; public sealed class ConstraintEnforcerTests { /// Verifies that read outside allowed subtree returns failure. + /// A task that represents the asynchronous operation. [Fact] public async Task CheckReadTagAsync_WhenOutsideReadSubtree_ReturnsFailure() { @@ -35,6 +36,7 @@ public sealed class ConstraintEnforcerTests } /// Verifies that write with high classification returns failure and audits. + /// A task that represents the asynchronous operation. [Fact] public async Task CheckWriteHandleAsync_WhenClassificationTooHigh_ReturnsFailureAndAudits() { @@ -84,6 +86,7 @@ public sealed class ConstraintEnforcerTests } /// A denial with no identity records the canonical "anonymous" actor. + /// A task that represents the asynchronous operation. [Fact] public async Task RecordDenialAsync_WithoutIdentity_UsesAnonymousActor() { @@ -103,6 +106,7 @@ public sealed class ConstraintEnforcerTests } /// Verifies that historized-only constraint requires historized attribute. + /// A task that represents the asynchronous operation. [Fact] public async Task CheckReadTagAsync_WithHistorizedOnly_RequiresRequestedAttributeToBeHistorized() { @@ -122,6 +126,7 @@ public sealed class ConstraintEnforcerTests } /// Verifies that alarm-only constraint requires alarm attribute. + /// A task that represents the asynchronous operation. [Fact] public async Task CheckReadTagAsync_WithAlarmOnly_RequiresRequestedAttributeToBeAlarm() { @@ -141,6 +146,7 @@ public sealed class ConstraintEnforcerTests } /// Verifies that attribute-only constraint fails closed for object tag. + /// A task that represents the asynchronous operation. [Fact] public async Task CheckReadTagAsync_WithAttributeOnlyConstraint_FailsClosedForObjectTag() { @@ -257,7 +263,7 @@ public sealed class ConstraintEnforcerTests private sealed class StubGalaxyHierarchyCache(GalaxyHierarchyCacheEntry current) : IGalaxyHierarchyCache { - /// Gets the current cache entry. + /// public GalaxyHierarchyCacheEntry Current { get; } = current; /// @@ -272,7 +278,10 @@ public sealed class ConstraintEnforcerTests /// Gets the recorded canonical audit events. public List Events { get; } = []; - /// + /// Appends the audit event to the recorded list. + /// The audit event to record. + /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. public Task WriteAsync(AuditEvent auditEvent, CancellationToken cancellationToken = default) { Events.Add(auditEvent); diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Security/Authorization/GatewayGrpcAuthorizationInterceptorTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Security/Authorization/GatewayGrpcAuthorizationInterceptorTests.cs index 0636d9a..243aa81 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Security/Authorization/GatewayGrpcAuthorizationInterceptorTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Security/Authorization/GatewayGrpcAuthorizationInterceptorTests.cs @@ -23,6 +23,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Security.Authorization; public sealed class GatewayGrpcAuthorizationInterceptorTests { /// Verifies that missing API key returns unauthenticated status. + /// A task that represents the asynchronous operation. [Fact] public async Task UnaryServerHandler_MissingApiKey_ReturnsUnauthenticated() { @@ -41,6 +42,7 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests } /// Verifies that invalid API key error does not expose raw credentials. + /// A task that represents the asynchronous operation. [Fact] public async Task UnaryServerHandler_InvalidApiKey_DoesNotExposeRawCredentialInStatus() { @@ -59,6 +61,7 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests } /// Verifies that valid key without required scope returns permission denied. + /// A task that represents the asynchronous operation. [Fact] public async Task UnaryServerHandler_ValidApiKeyMissingScope_ReturnsPermissionDenied() { @@ -77,6 +80,7 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests } /// Verifies that valid key with scope sets request identity for the handler. + /// A task that represents the asynchronous operation. [Fact] public async Task UnaryServerHandler_ValidApiKeyWithScope_SetsRequestIdentity() { @@ -103,6 +107,7 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests } /// Verifies that server stream handler requires proper scope. + /// A task that represents the asynchronous operation. [Fact] public async Task ServerStreamingServerHandler_ValidApiKeyMissingScope_ReturnsPermissionDenied() { @@ -122,6 +127,7 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests } /// Verifies that server stream handler allows streams with proper scope. + /// A task that represents the asynchronous operation. [Fact] public async Task ServerStreamingServerHandler_ValidApiKeyWithScope_AllowsStream() { @@ -147,6 +153,7 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests } /// Verifies that disabled authentication skips API key verification. + /// A task that represents the asynchronous operation. [Fact] public async Task UnaryServerHandler_AuthenticationDisabled_SkipsApiKeyVerification() { @@ -173,6 +180,7 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests /// that lacks the session:open scope, and asserts the interceptor denies the /// call with before the service runs. /// + /// A task that represents the asynchronous operation. [Fact] public async Task InterceptorComposedWithService_OpenSessionMissingScope_DeniesBeforeServiceRuns() { @@ -200,6 +208,7 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests /// that holds session:open, and asserts the service runs and observes the /// interceptor-supplied identity. /// + /// A task that represents the asynchronous operation. [Fact] public async Task InterceptorComposedWithService_OpenSessionWithScope_RunsServiceWithIdentity() { @@ -226,6 +235,7 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests /// because the wrapped command is a write, confirming command-scope mapping is /// enforced through the full composition. /// + /// A task that represents the asynchronous operation. [Fact] public async Task InterceptorComposedWithService_InvokeWriteCommandWithReadScope_DeniesBeforeServiceRuns() { @@ -261,6 +271,7 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests /// . Ack is a write-shaped mutation against /// alarm state, so it carries the same scope as MxCommandKind.Write. /// + /// A task that represents the asynchronous operation. [Fact] public async Task UnaryServerHandler_AcknowledgeAlarmMissingScope_ReturnsPermissionDenied() { @@ -279,6 +290,7 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests } /// Verifies that an API key holding invoke:write may call AcknowledgeAlarm. + /// A task that represents the asynchronous operation. [Fact] public async Task UnaryServerHandler_AcknowledgeAlarmWithScope_RunsHandler() { @@ -305,6 +317,7 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests /// lack . Active-alarm snapshots are part of the /// alarm/event surface and share the same scope as StreamEvents. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ServerStreamingServerHandler_QueryActiveAlarmsMissingScope_ReturnsPermissionDenied() { @@ -324,6 +337,7 @@ public sealed class GatewayGrpcAuthorizationInterceptorTests } /// Verifies that an API key holding events:read may call QueryActiveAlarms. + /// A task that represents the asynchronous operation. [Fact] public async Task ServerStreamingServerHandler_QueryActiveAlarmsWithScope_RunsHandler() { diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Security/Tls/KestrelTlsInspectorTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Security/Tls/KestrelTlsInspectorTests.cs index 4851090..ae43ac4 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Security/Tls/KestrelTlsInspectorTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Security/Tls/KestrelTlsInspectorTests.cs @@ -11,16 +11,19 @@ public sealed class KestrelTlsInspectorTests .AddInMemoryCollection(entries.ToDictionary(e => e.Key, e => (string?)e.Value)) .Build(); + /// Verifies RequiresGeneratedCertificate returns true when an HTTPS endpoint has no certificate configured. [Fact] public void RequiresGeneratedCertificate_True_WhenHttpsEndpointHasNoCertificate() => Assert.True(KestrelTlsInspector.RequiresGeneratedCertificate( Config(("Kestrel:Endpoints:Http:Url", "https://0.0.0.0:5120")))); + /// Verifies RequiresGeneratedCertificate returns false when all endpoints use plaintext. [Fact] public void RequiresGeneratedCertificate_False_WhenAllEndpointsPlaintext() => Assert.False(KestrelTlsInspector.RequiresGeneratedCertificate( Config(("Kestrel:Endpoints:Http:Url", "http://0.0.0.0:5120")))); + /// Verifies RequiresGeneratedCertificate returns false when the HTTPS endpoint has its own certificate. [Fact] public void RequiresGeneratedCertificate_False_WhenHttpsEndpointHasOwnCertificate() => Assert.False(KestrelTlsInspector.RequiresGeneratedCertificate( @@ -28,10 +31,12 @@ public sealed class KestrelTlsInspectorTests ("Kestrel:Endpoints:Http:Url", "https://0.0.0.0:5120"), ("Kestrel:Endpoints:Http:Certificate:Path", @"C:\certs\real.pfx")))); + /// Verifies RequiresGeneratedCertificate returns false when no endpoints are configured. [Fact] public void RequiresGeneratedCertificate_False_WhenNoEndpointsConfigured() => Assert.False(KestrelTlsInspector.RequiresGeneratedCertificate(Config())); + /// Verifies RequiresGeneratedCertificate returns false when the HTTPS endpoint identifies a certificate by thumbprint. [Fact] public void RequiresGeneratedCertificate_False_WhenHttpsEndpointHasThumbprintOnly() => Assert.False(KestrelTlsInspector.RequiresGeneratedCertificate( @@ -39,6 +44,7 @@ public sealed class KestrelTlsInspectorTests ("Kestrel:Endpoints:Https:Url", "https://0.0.0.0:5120"), ("Kestrel:Endpoints:Https:Certificate:Thumbprint", "AABBCCDDEEFF00112233445566778899AABBCCDD")))); + /// Verifies RequiresGeneratedCertificate returns false when the HTTPS endpoint identifies a certificate by subject. [Fact] public void RequiresGeneratedCertificate_False_WhenHttpsEndpointHasSubjectOnly() => Assert.False(KestrelTlsInspector.RequiresGeneratedCertificate( @@ -46,11 +52,13 @@ public sealed class KestrelTlsInspectorTests ("Kestrel:Endpoints:Https:Url", "https://0.0.0.0:5120"), ("Kestrel:Endpoints:Https:Certificate:Subject", "CN=myserver")))); + /// Verifies RequiresGeneratedCertificate returns true when the HTTPS URL is uppercase with no certificate. [Fact] public void RequiresGeneratedCertificate_True_WhenHttpsUrlIsUppercase() => Assert.True(KestrelTlsInspector.RequiresGeneratedCertificate( Config(("Kestrel:Endpoints:Https:Url", "HTTPS://0.0.0.0:5120")))); + /// Verifies RequiresGeneratedCertificate returns false when a Kestrel default certificate is configured. [Fact] public void RequiresGeneratedCertificate_False_WhenKestrelDefaultCertificateConfigured() => Assert.False(KestrelTlsInspector.RequiresGeneratedCertificate( @@ -58,6 +66,7 @@ public sealed class KestrelTlsInspectorTests ("Kestrel:Endpoints:Https:Url", "https://0.0.0.0:5120"), ("Kestrel:Certificates:Default:Path", @"C:\certs\default.pfx")))); + /// Verifies RequiresGeneratedCertificate returns true when mixed endpoints include one HTTPS endpoint with no certificate. [Fact] public void RequiresGeneratedCertificate_True_WhenMixedEndpointsAndOneHttpsHasNoCert() => Assert.True(KestrelTlsInspector.RequiresGeneratedCertificate( diff --git a/src/ZB.MOM.WW.MxGateway.Tests/Security/Tls/SelfSignedCertificateProviderTests.cs b/src/ZB.MOM.WW.MxGateway.Tests/Security/Tls/SelfSignedCertificateProviderTests.cs index 7b8dfc8..2d40f2a 100644 --- a/src/ZB.MOM.WW.MxGateway.Tests/Security/Tls/SelfSignedCertificateProviderTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Tests/Security/Tls/SelfSignedCertificateProviderTests.cs @@ -12,6 +12,7 @@ public sealed class SelfSignedCertificateProviderTests private static SelfSignedCertificateProvider CreateProvider(TlsOptions options, FakeTimeProvider time) => new(options, NullLogger.Instance, time); + /// Verifies that a generated certificate has the expected SANs, EKU, and validity period. [Fact] public void GenerateCertificate_HasExpectedSansEkuAndValidity() { @@ -39,6 +40,7 @@ public sealed class SelfSignedCertificateProviderTests o => o.Value == "1.3.6.1.5.5.7.3.1"); // serverAuth } + /// Verifies that LoadOrCreate generates, persists, and reuses the same certificate thumbprint. [Fact] public void LoadOrCreate_GeneratesPersistsAndReuses_SameThumbprint() { @@ -58,6 +60,7 @@ public sealed class SelfSignedCertificateProviderTests finally { Directory.Delete(dir, recursive: true); } } + /// Verifies that LoadOrCreate regenerates the certificate when the persisted certificate has expired. [Fact] public void LoadOrCreate_Regenerates_WhenPersistedCertExpired() { @@ -77,6 +80,7 @@ public sealed class SelfSignedCertificateProviderTests finally { Directory.Delete(dir, recursive: true); } } + /// Verifies that LoadOrCreate regenerates the certificate when the persisted file is corrupt. [Fact] public void LoadOrCreate_Regenerates_WhenPersistedFileCorrupt() { @@ -92,6 +96,7 @@ public sealed class SelfSignedCertificateProviderTests finally { Directory.Delete(dir, recursive: true); } } + /// Verifies that LoadOrCreate throws when the certificate is expired and regeneration is disabled. [Fact] public void LoadOrCreate_Throws_WhenExpiredAndRegenerateDisabled() { @@ -108,6 +113,7 @@ public sealed class SelfSignedCertificateProviderTests finally { Directory.Delete(dir, recursive: true); } } + /// Verifies that LoadOrCreate throws when the self-signed certificate path is blank. [Fact] public void LoadOrCreate_Throws_WhenSelfSignedCertPathBlank() { diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/Bootstrap/WorkerApplicationTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/Bootstrap/WorkerApplicationTests.cs index e1155c3..5b28223 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/Bootstrap/WorkerApplicationTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/Bootstrap/WorkerApplicationTests.cs @@ -143,10 +143,7 @@ public sealed class WorkerApplicationTests private sealed class SucceedingPipeClient : IWorkerPipeClient { - /// Runs the worker pipe client successfully. - /// Worker options. - /// Cancellation token. - /// Completed task. + /// public Task RunAsync( WorkerOptions options, CancellationToken cancellationToken = default) @@ -166,10 +163,7 @@ public sealed class WorkerApplicationTests _exception = exception; } - /// Runs the worker pipe client and throws configured exception. - /// Worker options. - /// Cancellation token. - /// Never completes; always throws. + /// public Task RunAsync( WorkerOptions options, CancellationToken cancellationToken = default) diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerFrameProtocolTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerFrameProtocolTests.cs index 4b60181..4e1ca76 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerFrameProtocolTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerFrameProtocolTests.cs @@ -14,6 +14,7 @@ public sealed class WorkerFrameProtocolTests private const string Nonce = "nonce-secret"; /// Verifies that valid envelopes round-trip through write and read. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAndReadAsync_WithValidEnvelope_RoundTripsFrame() { @@ -32,6 +33,7 @@ public sealed class WorkerFrameProtocolTests } /// Verifies that wrong protocol version throws mismatch error. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_WithWrongProtocolVersion_ThrowsProtocolVersionMismatch() { @@ -49,6 +51,7 @@ public sealed class WorkerFrameProtocolTests } /// Verifies that wrong session ID throws mismatch error. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_WithWrongSessionId_ThrowsSessionMismatch() { @@ -72,6 +75,7 @@ public sealed class WorkerFrameProtocolTests /// length prefix is the leading four bytes of the stream, so a four-zero-byte /// stream is exactly a frame declaring a zero-length payload. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_WithZeroLengthPayload_ThrowsMalformedLength() { @@ -93,6 +97,7 @@ public sealed class WorkerFrameProtocolTests /// A small maximum is configured so the rejection is asserted without /// allocating a multi-megabyte buffer. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_WithPayloadAboveConfiguredMaximum_ThrowsMessageTooLarge() { @@ -115,6 +120,7 @@ public sealed class WorkerFrameProtocolTests } /// Verifies that malformed payload throws invalid envelope error. + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_WithMalformedPayload_ThrowsInvalidEnvelope() { @@ -140,6 +146,7 @@ public sealed class WorkerFrameProtocolTests /// payload but only supplies 50 bytes, so the inner read loop sees /// bytesRead == 0 mid-frame. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_WhenStreamEndsMidFrame_ThrowsEndOfStream() { @@ -167,6 +174,7 @@ public sealed class WorkerFrameProtocolTests /// padded out to several hundred bytes — exceeds the limit /// without allocating anything large. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAsync_WithEnvelopeAboveConfiguredMaximum_ThrowsMessageTooLarge() { @@ -209,6 +217,7 @@ public sealed class WorkerFrameProtocolTests /// would weaken the writer against future serialisation /// regressions; this test makes its rationale visible. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAsync_WithEmptyEnvelope_ThrowsInvalidEnvelopeFromValidator() { @@ -233,6 +242,7 @@ public sealed class WorkerFrameProtocolTests } /// Verifies that concurrent writes produce complete serialized frames. + /// A task that represents the asynchronous operation. [Fact] public async Task WriteAsync_WithConcurrentCalls_SerializesCompleteFrames() { @@ -262,6 +272,7 @@ public sealed class WorkerFrameProtocolTests /// differing sizes back-to-back through one reader must parse each frame /// using only its own payload length, never trailing pooled bytes. /// + /// A task that represents the asynchronous operation. [Fact] public async Task ReadAsync_WithVaryingFrameSizes_ParsesEachFrameExactly() { diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerPipeClientTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerPipeClientTests.cs index 5c6dc6f..ec5c53c 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerPipeClientTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerPipeClientTests.cs @@ -15,6 +15,7 @@ namespace ZB.MOM.WW.MxGateway.Worker.Tests.Ipc; public sealed class WorkerPipeClientTests { /// Verifies that worker client connects and completes handshake. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_ConnectsToPipeAndCompletesHandshake() { @@ -83,6 +84,7 @@ public sealed class WorkerPipeClientTests } /// Verifies that worker client retries until pipe server becomes available. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_RetriesUntilPipeServerAppears() { @@ -127,6 +129,7 @@ public sealed class WorkerPipeClientTests } /// Verifies that worker client throws timeout if pipe never appears. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_WhenPipeNeverAppears_ThrowsTimeoutException() { diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerPipeSessionTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerPipeSessionTests.cs index 1300af4..2f70dbe 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerPipeSessionTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/Ipc/WorkerPipeSessionTests.cs @@ -20,6 +20,7 @@ public sealed class WorkerPipeSessionTests private const string Nonce = "nonce-secret"; /// Verifies that valid gateway hello triggers worker hello and ready responses. + /// A task that represents the asynchronous operation. [Fact] public async Task CompleteStartupHandshakeAsync_WithValidGatewayHello_SendsHelloThenReady() { @@ -51,6 +52,7 @@ public sealed class WorkerPipeSessionTests } /// Verifies that wrong nonce causes protocol violation fault before initialization. + /// A task that represents the asynchronous operation. [Fact] public async Task CompleteStartupHandshakeAsync_WithWrongNonce_FaultsBeforeInitialization() { @@ -79,6 +81,7 @@ public sealed class WorkerPipeSessionTests } /// Verifies that unsupported protocol version causes mismatch fault before initialization. + /// A task that represents the asynchronous operation. [Fact] public async Task CompleteStartupHandshakeAsync_WithWrongProtocol_FaultsBeforeInitialization() { @@ -106,6 +109,7 @@ public sealed class WorkerPipeSessionTests } /// Verifies that malformed frame causes protocol violation fault. + /// A task that represents the asynchronous operation. [Fact] public async Task CompleteStartupHandshakeAsync_WithMalformedFrame_WritesWorkerFault() { @@ -132,6 +136,7 @@ public sealed class WorkerPipeSessionTests } /// Verifies that MXAccess COM creation failure produces fault instead of ready. + /// A task that represents the asynchronous operation. [Fact] public async Task CompleteStartupHandshakeAsync_WhenMxAccessCreationFails_WritesFaultInsteadOfReady() { @@ -158,6 +163,7 @@ public sealed class WorkerPipeSessionTests } /// Verifies that heartbeat payload reflects current runtime snapshot. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_SendsHeartbeatPayloadFromRuntimeSnapshot() { @@ -211,6 +217,7 @@ public sealed class WorkerPipeSessionTests } /// Verifies that heartbeat reports current command correlation during execution. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_WhenCommandIsExecuting_HeartbeatReportsCurrentCorrelation() { @@ -258,6 +265,7 @@ public sealed class WorkerPipeSessionTests } /// Verifies that worker events are written to the pipe. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_WhenRuntimeHasEvents_WritesWorkerEventEnvelope() { @@ -297,6 +305,7 @@ public sealed class WorkerPipeSessionTests /// current-command correlation id empty to assert the genuine-hung /// path still fires. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_WhenStaActivityIsStale_WritesWatchdogFault() { @@ -343,6 +352,7 @@ public sealed class WorkerPipeSessionTests /// in-flight correlation id so the gateway can apply its own per-command /// timeout. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_WhenStaActivityIsStaleWithCommandInFlight_DoesNotWriteWatchdogFault() { @@ -403,6 +413,7 @@ public sealed class WorkerPipeSessionTests /// heartbeat State is derived from the session state, not forced to /// ExecutingCommand. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_AfterWatchdogFault_HeartbeatReportsFaultedState() { @@ -452,6 +463,7 @@ public sealed class WorkerPipeSessionTests /// has been stale for longer than HeartbeatStuckCeiling the /// watchdog DOES fire StaHung even with a command in flight. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_WhenStaActivityIsStaleBeyondCeilingWithCommandInFlight_WritesWatchdogFault() { @@ -494,6 +506,7 @@ public sealed class WorkerPipeSessionTests /// exception if the runtime-session factory returns null, rather than /// deferring the failure to an NRE on the next dereference. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_WhenRuntimeSessionFactoryReturnsNull_ThrowsDiagnosticException() { @@ -520,6 +533,7 @@ public sealed class WorkerPipeSessionTests /// Skipping disposal on the timed-out path leaked the STA thread and /// the MXAccess COM object. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_WhenShutdownTimesOut_StillDisposesRuntimeSession() { @@ -583,6 +597,7 @@ public sealed class WorkerPipeSessionTests } /// Verifies that shutdown drops late replies and sends shutdown ack. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_WhenShutdownArrivesDuringCommand_DropsLateReplyAndWritesShutdownAck() { @@ -623,6 +638,7 @@ public sealed class WorkerPipeSessionTests } /// Verifies that command exceptions after shutdown are dropped before ack. + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_WhenCommandThrowsAfterShutdown_DropsLateFaultAndWritesShutdownAck() { @@ -681,6 +697,7 @@ public sealed class WorkerPipeSessionTests /// returns true (keep reading), so a subsequent /// WorkerShutdown still produces the normal shutdown ack. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_WhenGatewaySendsWorkerCancel_ForwardsCorrelationIdToRuntimeSession() { @@ -723,6 +740,7 @@ public sealed class WorkerPipeSessionTests /// pre-handshake protocol violations); the contract this test pins /// is the exception type/error-code and message-loop exit. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_WhenGatewaySendsUnexpectedEnvelopeBodyAfterHandshake_ThrowsAndExitsMessageLoop() { @@ -761,6 +779,7 @@ public sealed class WorkerPipeSessionTests /// HeartbeatInterval. A long interval is configured so a delay-first /// loop would fail to deliver a heartbeat inside the assertion window. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_SendsFirstHeartbeatImmediatelyOnEnteringLoop() { @@ -804,6 +823,7 @@ public sealed class WorkerPipeSessionTests /// reply must be logged with a diagnostic rather than discarded /// silently, so a stuck gateway correlation wait can be traced. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RunAsync_WhenReplyIsDroppedAfterShutdown_LogsDiagnostic() { @@ -1093,17 +1113,13 @@ public sealed class WorkerPipeSessionTests } } - /// Records an informational log event. - /// The event name. - /// The event fields. + /// public void Information(string eventName, IReadOnlyDictionary fields) { Record(eventName, fields); } - /// Records an error log event. - /// The event name. - /// The event fields. + /// public void Error(string eventName, IReadOnlyDictionary fields) { Record(eventName, fields); @@ -1194,7 +1210,7 @@ public sealed class WorkerPipeSessionTests return new PipePair(gatewayStream, workerStream); } - /// Disposes pipe resources. + /// public void Dispose() { WorkerStream.Dispose(); diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/AlarmCommandExecutorTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/AlarmCommandExecutorTests.cs index 27b2f08..15493d8 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/AlarmCommandExecutorTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/AlarmCommandExecutorTests.cs @@ -371,28 +371,20 @@ public sealed class AlarmCommandExecutorTests /// Gets the last alarm filter prefix. public string? LastFilterPrefix { get; private set; } - /// Records a subscription. - /// The subscription expression. - /// The session identifier. + /// public void Subscribe(string subscription, string sessionId) { LastSubscription = subscription; LastSessionId = sessionId; } - /// Records an unsubscribe request. + /// public void Unsubscribe() { UnsubscribeCalled = true; } - /// Records an acknowledge request. - /// The alarm identifier. - /// The acknowledge comment. - /// The operator user name. - /// The operator node name. - /// The operator domain. - /// The operator full name. + /// public int Acknowledge( Guid alarmGuid, string comment, string operatorUser, string operatorNode, string operatorDomain, string operatorFullName) @@ -406,15 +398,7 @@ public sealed class AlarmCommandExecutorTests return AcknowledgeReturn; } - /// Records an acknowledge by name request. - /// The alarm name. - /// The provider name. - /// The group name. - /// The acknowledge comment. - /// The operator user name. - /// The operator node name. - /// The operator domain. - /// The operator full name. + /// public int AcknowledgeByName( string alarmName, string providerName, string groupName, string comment, string operatorUser, string operatorNode, @@ -428,8 +412,7 @@ public sealed class AlarmCommandExecutorTests /// Gets the last acknowledge by name tuple. public (string Name, string Provider, string Group)? LastAckByNameTuple { get; private set; } - /// Queries the active alarms with the given filter prefix. - /// The alarm filter prefix for the query. + /// public IReadOnlyList QueryActive(string? alarmFilterPrefix) { LastFilterPrefix = alarmFilterPrefix; @@ -439,7 +422,7 @@ public sealed class AlarmCommandExecutorTests /// Gets the number of poll calls. public int PollCount { get; private set; } - /// Increments the poll count. + /// public void PollOnce() { PollCount++; diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/AlarmCommandHandlerTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/AlarmCommandHandlerTests.cs index fdd004a..dd40ef9 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/AlarmCommandHandlerTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/AlarmCommandHandlerTests.cs @@ -310,8 +310,7 @@ public sealed class AlarmCommandHandlerTests /// Gets a value indicating whether the consumer has been disposed. public bool Disposed { get; private set; } - /// Subscribes to alarms with the given subscription string. - /// The subscription reference. + /// public void Subscribe(string subscription) { LastSubscription = subscription; @@ -321,13 +320,7 @@ public sealed class AlarmCommandHandlerTests } } - /// Acknowledges an alarm by GUID. - /// The alarm GUID. - /// The acknowledgment comment. - /// The operator name. - /// The operator node. - /// The operator domain. - /// The operator full name. + /// public int AcknowledgeByGuid( Guid alarmGuid, string ackComment, string ackOperatorName, string ackOperatorNode, string ackOperatorDomain, string ackOperatorFullName) @@ -337,15 +330,7 @@ public sealed class AlarmCommandHandlerTests return AcknowledgeReturn; } - /// Acknowledges an alarm by name. - /// The alarm name. - /// The provider name. - /// The alarm group name. - /// The acknowledgment comment. - /// The operator name. - /// The operator node. - /// The operator domain. - /// The operator full name. + /// public int AcknowledgeByName( string alarmName, string providerName, string groupName, string ackComment, string ackOperatorName, string ackOperatorNode, @@ -359,13 +344,13 @@ public sealed class AlarmCommandHandlerTests /// Gets the last acknowledge-by-name parameters. public (string Name, string Provider, string Group)? LastAckByNameTuple { get; private set; } - /// Returns a snapshot of active alarms. + /// public IReadOnlyList SnapshotActiveAlarms() => SnapshotResult; /// Gets the number of times polled. public int PollCount { get; private set; } - /// Polls once for alarm updates. + /// public void PollOnce() { PollCount++; diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/AlarmDispatcherTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/AlarmDispatcherTests.cs index 18af656..6a5dc48 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/AlarmDispatcherTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/AlarmDispatcherTests.cs @@ -303,20 +303,13 @@ public sealed class AlarmDispatcherTests AlarmTransitionEmitted?.Invoke(this, transition); } - /// Records the subscription reference. - /// The subscription reference. + /// public void Subscribe(string subscription) { LastSubscription = subscription; } - /// Records an acknowledge-by-GUID call with operator identity. - /// The alarm GUID. - /// The acknowledgment comment. - /// The operator name. - /// The operator node. - /// The operator domain. - /// The operator full name. + /// public int AcknowledgeByGuid( Guid alarmGuid, string ackComment, @@ -334,15 +327,7 @@ public sealed class AlarmDispatcherTests return AcknowledgeReturn; } - /// Records an acknowledge-by-name call with alarm name, provider, and group. - /// The alarm name. - /// The provider name. - /// The alarm group name. - /// The acknowledgment comment. - /// The operator name. - /// The operator node. - /// The operator domain. - /// The operator full name. + /// public int AcknowledgeByName( string alarmName, string providerName, string groupName, string ackComment, string ackOperatorName, string ackOperatorNode, @@ -356,7 +341,7 @@ public sealed class AlarmDispatcherTests /// Gets the last acknowledge-by-name tuple (alarm name, provider, group). public (string Name, string Provider, string Group)? LastAckByNameTuple { get; private set; } - /// Returns the current snapshot result collection. + /// public IReadOnlyList SnapshotActiveAlarms() { return SnapshotResult; @@ -365,7 +350,7 @@ public sealed class AlarmDispatcherTests /// Gets the count of poll operations. public int PollCount { get; private set; } - /// Increments the poll count. + /// public void PollOnce() { PollCount++; diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessComServerTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessComServerTests.cs index e821cac..37f3501 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessComServerTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessComServerTests.cs @@ -91,8 +91,7 @@ public sealed class MxAccessComServerTests /// Gets the recorded method calls as strings. public IReadOnlyList Calls => calls.ToArray(); - /// Records a Register call and returns the configured handle. - /// The client name to record. + /// public int Register(string clientName) { calls.Add($"Register:{clientName}"); @@ -105,103 +104,69 @@ public sealed class MxAccessComServerTests return registerHandle; } - /// Records an Unregister call. - /// The MXAccess server handle. + /// public void Unregister(int serverHandle) { calls.Add($"Unregister:{serverHandle}"); } - /// Records an AddItem call and returns zero. - /// The MXAccess server handle. - /// The item definition string to record. + /// public int AddItem(int serverHandle, string itemDefinition) { calls.Add($"AddItem:{serverHandle}:{itemDefinition}"); return 0; } - /// Records an AddItem2 call and returns zero. - /// The MXAccess server handle. - /// The item definition string to record. - /// The item context string to record. + /// public int AddItem2(int serverHandle, string itemDefinition, string itemContext) { calls.Add($"AddItem2:{serverHandle}:{itemDefinition}:{itemContext}"); return 0; } - /// Records a RemoveItem call. - /// The MXAccess server handle. - /// The MXAccess item handle. + /// public void RemoveItem(int serverHandle, int itemHandle) { calls.Add($"RemoveItem:{serverHandle}:{itemHandle}"); } - /// Records an Advise call. - /// The MXAccess server handle. - /// The MXAccess item handle. + /// public void Advise(int serverHandle, int itemHandle) { calls.Add($"Advise:{serverHandle}:{itemHandle}"); } - /// Records an UnAdvise call. - /// The MXAccess server handle. - /// The MXAccess item handle. + /// public void UnAdvise(int serverHandle, int itemHandle) { calls.Add($"UnAdvise:{serverHandle}:{itemHandle}"); } - /// Records an AdviseSupervisory call. - /// The MXAccess server handle. - /// The MXAccess item handle. + /// public void AdviseSupervisory(int serverHandle, int itemHandle) { calls.Add($"AdviseSupervisory:{serverHandle}:{itemHandle}"); } - /// Records a Write call. - /// The MXAccess server handle. - /// The MXAccess item handle. - /// The value to write. - /// The user identifier. + /// public void Write(int serverHandle, int itemHandle, object? value, int userId) { calls.Add($"Write:{serverHandle}:{itemHandle}:{value}:{userId}"); } - /// Records a Write2 call. - /// The MXAccess server handle. - /// The MXAccess item handle. - /// The value to write. - /// The timestamp value. - /// The user identifier. + /// public void Write2(int serverHandle, int itemHandle, object? value, object? timestamp, int userId) { calls.Add($"Write2:{serverHandle}:{itemHandle}:{value}:{timestamp}:{userId}"); } - /// Records a WriteSecured call. - /// The MXAccess server handle. - /// The MXAccess item handle. - /// The current user identifier. - /// The verifier user identifier. - /// The value to write. + /// public void WriteSecured(int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object? value) { calls.Add($"WriteSecured:{serverHandle}:{itemHandle}:{currentUserId}:{verifierUserId}:{value}"); } - /// Records a WriteSecured2 call. - /// The MXAccess server handle. - /// The MXAccess item handle. - /// The current user identifier. - /// The verifier user identifier. - /// The value to write. - /// The timestamp value. + /// public void WriteSecured2( int serverHandle, int itemHandle, int currentUserId, int verifierUserId, object? value, object? timestamp) { diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessCommandExecutorTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessCommandExecutorTests.cs index f9d19fa..8f93f8a 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessCommandExecutorTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessCommandExecutorTests.cs @@ -14,6 +14,7 @@ namespace ZB.MOM.WW.MxGateway.Worker.Tests.MxAccess; public sealed class MxAccessCommandExecutorTests { /// Verifies that Register command calls MXAccess on the STA thread and preserves the server handle. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_Register_CallsMxAccessOnStaAndPreservesServerHandle() { @@ -40,6 +41,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that Unregister command calls MXAccess on the STA thread and removes the tracked server handle. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_Unregister_CallsMxAccessOnStaAndRemovesTrackedServerHandle() { @@ -59,6 +61,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that Unregister preserves the HResult when MXAccess throws and does not rewrite the failure. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_UnregisterWhenMxAccessThrows_PreservesHResultAndDoesNotRewriteFailure() { @@ -86,6 +89,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that AddItem command calls MXAccess on the STA thread and tracks the item handle. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_AddItem_CallsMxAccessOnStaAndTracksItemHandle() { @@ -123,6 +127,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that AddItem2 command passes the context exactly and tracks the item handle. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_AddItem2_PassesContextExactlyAndTracksItemHandle() { @@ -160,6 +165,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that RemoveItem command calls MXAccess on the STA thread and removes the tracked item handle. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_RemoveItem_CallsMxAccessOnStaAndRemovesTrackedItemHandle() { @@ -188,6 +194,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that RemoveItem removes tracked advice after MXAccess succeeds on an advised handle. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_RemoveItemWithAdvisedHandle_RemovesTrackedAdviceAfterMxAccessSucceeds() { @@ -213,6 +220,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that RemoveItem preserves the HResult and keeps the tracked item handle when using a cross-server handle. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_RemoveItemWithCrossServerHandle_PreservesHResultAndKeepsTrackedItemHandle() { @@ -247,6 +255,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that AddItem2 preserves the HResult when MXAccess throws and does not track the item handle. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_AddItem2WhenMxAccessThrows_PreservesHResultAndDoesNotTrackItemHandle() { @@ -276,6 +285,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that Advise command calls MXAccess on the STA thread and tracks plain advice. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_Advise_CallsMxAccessOnStaAndTracksPlainAdvice() { @@ -309,6 +319,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that AdviseSupervisory calls a distinct MXAccess method and tracks supervisory advice. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_AdviseSupervisory_CallsDistinctMxAccessMethodAndTracksSupervisoryAdvice() { @@ -341,6 +352,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that UnAdvise command calls MXAccess on the STA thread and removes the tracked advice. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_UnAdvise_CallsMxAccessOnStaAndRemovesTrackedAdvice() { @@ -369,6 +381,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that Advise preserves the HResult when MXAccess throws and does not track the advice. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_AdviseWhenMxAccessThrows_PreservesHResultAndDoesNotTrackAdvice() { @@ -399,6 +412,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that UnAdvise preserves the HResult when MXAccess throws and keeps the tracked advice. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_UnAdviseWhenMxAccessThrows_PreservesHResultAndKeepsTrackedAdvice() { @@ -433,6 +447,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that SubscribeBulk runs sequential MXAccess calls and returns per-item results. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_SubscribeBulk_RunsSequentialMxAccessCallsAndReturnsPerItemResults() { @@ -478,6 +493,7 @@ public sealed class MxAccessCommandExecutorTests /// one BulkWriteResult per entry in input order, including a per-entry COM /// failure surfaced as WasSuccessful=false with the underlying HRESULT. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_WriteBulk_RunsSequentialWritesAndReturnsPerEntryResults() { @@ -530,6 +546,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that Write2Bulk forwards value AND timestamp to each per-entry Write2. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_Write2Bulk_ForwardsValueAndTimestampPerEntry() { @@ -556,6 +573,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that WriteSecuredBulk forwards both user ids per entry. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_WriteSecuredBulk_ForwardsUserIdsPerEntry() { @@ -580,6 +598,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that WriteSecured2Bulk forwards user ids, value, and timestamp per entry. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_WriteSecured2Bulk_ForwardsUserIdsValueAndTimestampPerEntry() { @@ -612,6 +631,7 @@ public sealed class MxAccessCommandExecutorTests /// The fake COM object never fires events so the wait always times out — but /// the lifecycle calls must still happen, in order, on the STA. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_ReadBulk_WhenTagNotCached_TakesSnapshotLifecycleAndTimesOut() { @@ -650,6 +670,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that ReadBulk with no payload returns an invalid request error. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_ReadBulkWithoutPayload_ReturnsInvalidRequest() { @@ -671,6 +692,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that UnsubscribeBulk removes items after UnAdvise failure. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_UnsubscribeBulk_RemovesItemAfterUnAdviseFailure() { @@ -700,6 +722,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that ShutdownGracefullyAsync cleans up handles in advice, item, server order. + /// A task that represents the asynchronous operation. [Fact] public async Task ShutdownGracefullyAsync_CleansHandlesInAdviceItemServerOrder() { @@ -725,6 +748,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that ShutdownGracefullyAsync records cleanup failures and continues. + /// A task that represents the asynchronous operation. [Fact] public async Task ShutdownGracefullyAsync_RecordsCleanupFailuresAndContinues() { @@ -753,6 +777,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that Register without payload returns an invalid request error. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_RegisterWithoutPayload_ReturnsInvalidRequest() { @@ -774,6 +799,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that AddItem without payload returns an invalid request error. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_AddItemWithoutPayload_ReturnsInvalidRequest() { @@ -795,6 +821,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that Advise without payload returns an invalid request error. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_AdviseWithoutPayload_ReturnsInvalidRequest() { @@ -816,6 +843,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that Write dispatches the converted value to MXAccess on the STA thread. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_Write_CallsMxAccessOnStaWithConvertedValue() { @@ -838,6 +866,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that Write2 forwards the converted value and timestamp to MXAccess. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_Write2_ForwardsValueAndTimestamp() { @@ -860,6 +889,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that WriteSecured forwards the operator and verifier user ids to MXAccess. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_WriteSecured_ForwardsUserIds() { @@ -881,6 +911,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that WriteSecured2 forwards user ids, value, and timestamp to MXAccess. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_WriteSecured2_ForwardsUserIdsValueAndTimestamp() { @@ -904,6 +935,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that Write without a payload returns an invalid request error. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_WriteWithoutPayload_ReturnsInvalidRequest() { @@ -926,6 +958,7 @@ public sealed class MxAccessCommandExecutorTests } /// Verifies that Write without a value returns an invalid request error. + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_WriteWithoutValue_ReturnsInvalidRequest() { @@ -1541,9 +1574,7 @@ public sealed class MxAccessCommandExecutorTests /// Gets the list of operations performed on this fake object. public IReadOnlyList OperationNames => operationNames.ToArray(); - /// Registers a client and returns a server handle. - /// Name of the client to register. - /// The server handle for the registered client. + /// public int Register(string clientName) { operationNames.Add($"Register:{clientName}"); @@ -1553,8 +1584,7 @@ public sealed class MxAccessCommandExecutorTests return registerHandle; } - /// Unregisters a server and tracks the operation. - /// Server handle to unregister. + /// public void Unregister(int serverHandle) { operationNames.Add($"Unregister:{serverHandle}"); @@ -1567,10 +1597,7 @@ public sealed class MxAccessCommandExecutorTests } } - /// Adds an item to the server and returns its item handle. - /// Server handle to add the item to. - /// Item definition string. - /// The item handle for the added item. + /// public int AddItem( int serverHandle, string itemDefinition) @@ -1588,11 +1615,7 @@ public sealed class MxAccessCommandExecutorTests return addItemHandle; } - /// Adds an item to the server with context and returns its item handle. - /// Server handle to add the item to. - /// Item definition string. - /// Item context string. - /// The item handle for the added item. + /// public int AddItem2( int serverHandle, string itemDefinition, @@ -1612,9 +1635,7 @@ public sealed class MxAccessCommandExecutorTests return addItem2Handle; } - /// Removes an item from the server and tracks the operation. - /// Server handle from which to remove the item. - /// Item handle to remove. + /// public void RemoveItem( int serverHandle, int itemHandle) @@ -1630,9 +1651,7 @@ public sealed class MxAccessCommandExecutorTests } } - /// Advises on item changes and tracks the operation. - /// Server handle for the advisory subscription. - /// Item handle to advise on. + /// public void Advise( int serverHandle, int itemHandle) @@ -1648,9 +1667,7 @@ public sealed class MxAccessCommandExecutorTests } } - /// Removes an item advice subscription and tracks the operation. - /// Server handle from which to remove the subscription. - /// Item handle to remove advice from. + /// public void UnAdvise( int serverHandle, int itemHandle) @@ -1666,9 +1683,7 @@ public sealed class MxAccessCommandExecutorTests } } - /// Advises supervisory on item changes and tracks the operation. - /// Server handle for the supervisory subscription. - /// Item handle to advise supervisory on. + /// public void AdviseSupervisory( int serverHandle, int itemHandle) @@ -1708,11 +1723,7 @@ public sealed class MxAccessCommandExecutorTests /// Gets the thread ID on which the most recent write was called. public int? WriteThreadId { get; private set; } - /// Writes a value to an item and tracks the operation. - /// Server handle for the write. - /// Item handle to write to. - /// Value to write. - /// MXAccess user id for the write. + /// public void Write( int serverHandle, int itemHandle, @@ -1728,12 +1739,7 @@ public sealed class MxAccessCommandExecutorTests ThrowIfWriteFailureConfigured(itemHandle); } - /// Writes a timestamped value to an item and tracks the operation. - /// Server handle for the write. - /// Item handle to write to. - /// Value to write. - /// Source timestamp for the write. - /// MXAccess user id for the write. + /// public void Write2( int serverHandle, int itemHandle, @@ -1751,12 +1757,7 @@ public sealed class MxAccessCommandExecutorTests ThrowIfWriteFailureConfigured(itemHandle); } - /// Performs a secured write to an item and tracks the operation. - /// Server handle for the write. - /// Item handle to write to. - /// Operator user id. - /// Verifier user id. - /// Value to write. + /// public void WriteSecured( int serverHandle, int itemHandle, @@ -1774,13 +1775,7 @@ public sealed class MxAccessCommandExecutorTests ThrowIfWriteFailureConfigured(itemHandle); } - /// Performs a secured timestamped write to an item and tracks the operation. - /// Server handle for the write. - /// Item handle to write to. - /// Operator user id. - /// Verifier user id. - /// Value to write. - /// Source timestamp for the write. + /// public void WriteSecured2( int serverHandle, int itemHandle, @@ -1825,8 +1820,7 @@ public sealed class MxAccessCommandExecutorTests /// Gets the fake COM object. public FakeMxAccessComObject FakeComObject { get; } - /// Creates and returns the fake MXAccess COM object. - /// The fake COM object. + /// public object Create() { return FakeComObject; diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessLiveComCreationTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessLiveComCreationTests.cs index 9479fb1..404a8fd 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessLiveComCreationTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessLiveComCreationTests.cs @@ -15,6 +15,7 @@ public sealed class MxAccessLiveComCreationTests private const string DefaultLiveAddItem2Context = "TestChildObject"; /// Verifies that StartAsync creates the installed MXAccess COM object on the STA thread when opted in. + /// A task that represents the asynchronous operation. [LiveMxAccessFact] public async Task StartAsync_WhenOptedIn_CreatesInstalledMxAccessComObjectOnSta() { @@ -24,6 +25,7 @@ public sealed class MxAccessLiveComCreationTests } /// Verifies that Register and Unregister round-trip server handles with installed MXAccess. + /// A task that represents the asynchronous operation. [LiveMxAccessFact] public async Task RegisterAndUnregister_WhenOptedIn_RoundTripsInstalledMxAccessServerHandle() { @@ -61,6 +63,7 @@ public sealed class MxAccessLiveComCreationTests } /// Verifies that AddItem and RemoveItem round-trip item handles with installed MXAccess. + /// A task that represents the asynchronous operation. [LiveMxAccessFact] public async Task AddItemAndRemoveItem_WhenOptedIn_RoundTripsInstalledMxAccessItemHandle() { @@ -129,6 +132,7 @@ public sealed class MxAccessLiveComCreationTests } /// Verifies that AddItem2 and RemoveItem preserve item context with installed MXAccess. + /// A task that represents the asynchronous operation. [LiveMxAccessFact] public async Task AddItem2AndRemoveItem_WhenOptedIn_PreservesContextForInstalledMxAccess() { @@ -198,6 +202,7 @@ public sealed class MxAccessLiveComCreationTests } /// Verifies that Advise and UnAdvise round-trip subscriptions with installed MXAccess. + /// A task that represents the asynchronous operation. [LiveMxAccessFact] public async Task AdviseAndUnAdvise_WhenOptedIn_RoundTripsInstalledMxAccessSubscription() { diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessStaSessionTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessStaSessionTests.cs index fcf563a..584437a 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessStaSessionTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessStaSessionTests.cs @@ -18,6 +18,7 @@ public sealed class MxAccessStaSessionTests /// /// Verifies that StartAsync creates the MXAccess COM object and attaches the event sink on the STA thread. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StartAsync_CreatesComObjectAndAttachesEventSinkOnStaThread() { @@ -42,6 +43,7 @@ public sealed class MxAccessStaSessionTests /// /// Verifies that StartAsync maps creation exceptions with HResult when the factory fails. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StartAsync_WhenFactoryFails_MapsCreationExceptionWithHResult() { @@ -63,6 +65,7 @@ public sealed class MxAccessStaSessionTests /// /// Verifies that Dispose detaches the event sink on the STA thread. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Dispose_DetachesEventSinkOnStaThread() { @@ -116,9 +119,7 @@ public sealed class MxAccessStaSessionTests /// public ApartmentState? CreateApartmentState { get; private set; } - /// - /// Creates the COM object or throws the configured exception. - /// + /// public object Create() { CreateThreadId = Thread.CurrentThread.ManagedThreadId; @@ -158,11 +159,7 @@ public sealed class MxAccessStaSessionTests /// public string? SessionId { get; private set; } - /// - /// Attaches the MXAccess COM object and records thread context. - /// - /// MXAccess COM object to attach. - /// Identifier of the session. + /// public void Attach( object mxAccessComObject, string sessionId) @@ -172,9 +169,7 @@ public sealed class MxAccessStaSessionTests SessionId = sessionId; } - /// - /// Detaches the MXAccess COM object and records thread context. - /// + /// public void Detach() { DetachThreadId = Thread.CurrentThread.ManagedThreadId; @@ -188,6 +183,7 @@ public sealed class MxAccessStaSessionTests /// This proves the fix in WorkerPipeSession (and the new internal constructor) correctly /// wires the factory rather than leaving alarmCommandHandler null. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StartAsync_WithAlarmCommandHandlerFactory_SubscribeAlarmsCommandReachesHandler() { @@ -231,6 +227,7 @@ public sealed class MxAccessStaSessionTests /// test fails if the diagnostic regresses to a misleading message that still /// happens to contain the word "alarm". /// + /// A task that represents the asynchronous operation. [Fact] public async Task StartAsync_WithoutAlarmCommandHandlerFactory_SubscribeAlarmsReturnsInvalidRequest() { @@ -267,6 +264,7 @@ public sealed class MxAccessStaSessionTests /// loop calls PollOnce on the handler via the STA within a reasonable timeout. /// This proves polling is driven by the STA rather than the consumer's internal timer. /// + /// A task that represents the asynchronous operation. [Fact] public async Task StartAsync_WithAlarmCommandHandlerFactory_PollOnceCalledViaSta() { @@ -304,6 +302,7 @@ public sealed class MxAccessStaSessionTests /// immediately after Dispose and stays frozen — deterministic, with no /// elapsed-time "no further polls" window that a slow agent could race. /// + /// A task that represents the asynchronous operation. [Fact] public async Task Dispose_StopsAlarmPollLoop() { @@ -352,6 +351,7 @@ public sealed class MxAccessStaSessionTests /// alarm subscription becomes observable on the IPC fault path instead /// of silently faulting the never-awaited poll task. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RunAlarmPollLoop_WhenPollOnceThrows_RecordsFaultOnEventQueue() { @@ -399,6 +399,7 @@ public sealed class MxAccessStaSessionTests /// from PollOnce must reach /// the fault-recording arm and become observable on the event queue. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RunAlarmPollLoop_WhenPollOnceThrowsInvalidOperation_RecordsFaultOnEventQueue() { @@ -495,52 +496,35 @@ public sealed class MxAccessStaSessionTests get { lock (gate) return lastPollThreadId; } } - /// Subscribes to alarm events. - /// The subscription descriptor. - /// The session identifier. + /// public void Subscribe(string subscription, string sessionId) { IsSubscribed = true; LastSubscription = subscription; } - /// Unsubscribes from alarm events. + /// public void Unsubscribe() { IsSubscribed = false; } - /// Acknowledges an alarm by guid. - /// The alarm GUID. - /// The acknowledgment comment. - /// The operator user name. - /// The operator node name. - /// The operator domain. - /// The operator full name. + /// public int Acknowledge(Guid alarmGuid, string comment, string operatorUser, string operatorNode, string operatorDomain, string operatorFullName) => 0; - /// Acknowledges an alarm by name. - /// The alarm name. - /// The provider name. - /// The alarm group name. - /// The acknowledgment comment. - /// The operator user name. - /// The operator node name. - /// The operator domain. - /// The operator full name. + /// public int AcknowledgeByName(string alarmName, string providerName, string groupName, string comment, string operatorUser, string operatorNode, string operatorDomain, string operatorFullName) => 0; - /// Queries active alarms. - /// Optional alarm name filter prefix. + /// public IReadOnlyList QueryActive(string? alarmFilterPrefix) => Array.Empty(); - /// Polls for alarm events once. + /// public void PollOnce() { lock (gate) diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessValueCacheTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessValueCacheTests.cs index 14b2a8e..7e5cb4f 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessValueCacheTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/MxAccess/MxAccessValueCacheTests.cs @@ -115,6 +115,7 @@ public sealed class MxAccessValueCacheTests } /// Verifies that TryWaitForUpdate returns true when the cache is updated after the baseline. + /// A task that represents the asynchronous operation. [Fact] public async Task TryWaitForUpdate_ReturnsTrue_WhenSetFiresAfterBaselineVersion() { diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/Sta/StaCommandDispatcherTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/Sta/StaCommandDispatcherTests.cs index 7987190..ee189c6 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/Sta/StaCommandDispatcherTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/Sta/StaCommandDispatcherTests.cs @@ -18,6 +18,7 @@ public sealed class StaCommandDispatcherTests /// /// Verifies commands execute on the STA thread in queue order. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_ExecutesCommandsOnStaInQueueOrder() { @@ -41,6 +42,7 @@ public sealed class StaCommandDispatcherTests /// /// Verifies executor exceptions are captured as HResult in the reply without exposing message details. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_WhenExecutorThrows_ReturnsFailureReplyWithHResult() { @@ -64,6 +66,7 @@ public sealed class StaCommandDispatcherTests /// /// Verifies cancellation before execution prevents the command from running. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_WhenCanceledBeforeExecution_ReturnsCanceledReplyWithoutExecuting() { @@ -96,6 +99,7 @@ public sealed class StaCommandDispatcherTests /// observed and ignored" from "cancel never checked"; it only proves the /// in-flight command is not aborted. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_WhenCanceledWhileExecuting_DoesNotAbortInFlightCommand() { @@ -121,6 +125,7 @@ public sealed class StaCommandDispatcherTests /// /// Verifies shutdown rejects new dispatch attempts. /// + /// A task that represents the asynchronous operation. [Fact] public async Task DispatchAsync_WhenShutdownRequested_RejectsNewCommands() { @@ -138,6 +143,7 @@ public sealed class StaCommandDispatcherTests /// /// Verifies shutdown allows the current command to complete but rejects queued commands. /// + /// A task that represents the asynchronous operation. [Fact] public async Task RequestShutdown_RejectsQueuedCommandButLetsCurrentCommandFinish() { @@ -162,6 +168,7 @@ public sealed class StaCommandDispatcherTests /// /// Verifies heartbeat reports current command correlation ID and pending command count. /// + /// A task that represents the asynchronous operation. [Fact] public async Task PopulateHeartbeat_ReportsCurrentCorrelationAndPendingCount() { diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/Sta/StaMessagePumpTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/Sta/StaMessagePumpTests.cs index b9eeba8..3f4e30a 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/Sta/StaMessagePumpTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/Sta/StaMessagePumpTests.cs @@ -37,6 +37,7 @@ public sealed class StaMessagePumpTests /// /// Verifies that WaitForWorkOrMessages returns promptly when the wake event is already signalled. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WaitForWorkOrMessages_WakeEventAlreadySignalled_ReturnsImmediately() { @@ -60,6 +61,7 @@ public sealed class StaMessagePumpTests /// /// Verifies that WaitForWorkOrMessages wakes when the wake event is signalled from another thread. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WaitForWorkOrMessages_WakeEventSignalledDuringWait_Returns() { @@ -89,6 +91,7 @@ public sealed class StaMessagePumpTests /// /// Verifies that WaitForWorkOrMessages returns on timeout when the wake event is never signalled. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WaitForWorkOrMessages_WakeEventNeverSignalled_ReturnsAfterTimeout() { @@ -113,6 +116,7 @@ public sealed class StaMessagePumpTests /// /// Verifies that a zero timeout (the TimeSpan.Zero conversion branch) returns without blocking. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WaitForWorkOrMessages_ZeroTimeout_ReturnsWithoutBlocking() { @@ -137,6 +141,7 @@ public sealed class StaMessagePumpTests /// /// Verifies that PumpPendingMessages returns zero when the STA thread message queue is empty. /// + /// A task that represents the asynchronous operation. [Fact] public async Task PumpPendingMessages_NoMessagesPosted_ReturnsZero() { @@ -155,6 +160,7 @@ public sealed class StaMessagePumpTests /// /// Verifies that PumpPendingMessages dispatches and counts messages posted to the STA thread. /// + /// A task that represents the asynchronous operation. [Fact] public async Task PumpPendingMessages_MessagesPostedToStaThread_ReturnsCountProcessed() { @@ -179,6 +185,7 @@ public sealed class StaMessagePumpTests /// /// Verifies that WaitForWorkOrMessages returns once a Windows message is posted to the STA thread. /// + /// A task that represents the asynchronous operation. [Fact] public async Task WaitForWorkOrMessages_WindowsMessagePosted_ReturnsForInputAvailable() { diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/Sta/StaRuntimeTests.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/Sta/StaRuntimeTests.cs index ebccf33..17ccb2b 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/Sta/StaRuntimeTests.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/Sta/StaRuntimeTests.cs @@ -8,6 +8,7 @@ namespace ZB.MOM.WW.MxGateway.Worker.Tests.Sta; public sealed class StaRuntimeTests { /// Verifies that InvokeAsync executes commands on the STA thread. + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_ExecutesCommandOnStaThread() { @@ -35,6 +36,7 @@ public sealed class StaRuntimeTests /// correct dispatch past an arbitrary millisecond budget, which would be a /// false failure. /// + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_WakesIdlePumpForQueuedCommand() { @@ -84,6 +86,7 @@ public sealed class StaRuntimeTests } /// Verifies that InvokeAsync faults the returned task when a command raises an exception without stopping the runtime. + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_CommandException_FaultsReturnedTaskWithoutStoppingRuntime() { @@ -109,6 +112,7 @@ public sealed class StaRuntimeTests /// assertion. The test pins the exact type so a regression that /// reverts to a plain InvalidOperationException fails here. /// + /// A task that represents the asynchronous operation. [Fact] public async Task InvokeAsync_AfterShutdown_ReturnsFaultedTask() { @@ -164,14 +168,14 @@ public sealed class StaRuntimeTests /// The thread ID where Uninitialize was called. public int? UninitializeThreadId { get; private set; } - /// Initializes the COM apartment and records the calling thread. + /// public void Initialize() { InitializeCount++; InitializeThreadId = Thread.CurrentThread.ManagedThreadId; } - /// Uninitializes the COM apartment and records the calling thread. + /// public void Uninitialize() { UninitializeCount++; diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/TestSupport/FakeRuntimeSession.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/TestSupport/FakeRuntimeSession.cs index e361819..0632629 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/TestSupport/FakeRuntimeSession.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/TestSupport/FakeRuntimeSession.cs @@ -46,11 +46,7 @@ internal sealed class FakeRuntimeSession : IWorkerRuntimeSession /// Gets a value indicating whether Dispose was called. public bool Disposed { get; private set; } - /// Starts the worker session with the given session ID and process ID. - /// The session identifier. - /// The worker process ID. - /// Cancellation token. - /// Worker ready response. + /// public Task StartAsync( string sessionId, int workerProcessId, @@ -65,9 +61,7 @@ internal sealed class FakeRuntimeSession : IWorkerRuntimeSession }); } - /// Dispatches a command to the STA thread. - /// The command to dispatch. - /// The command reply. + /// public Task DispatchAsync(StaCommand command) { return Task.Run( @@ -112,8 +106,7 @@ internal sealed class FakeRuntimeSession : IWorkerRuntimeSession }); } - /// Captures current heartbeat snapshot. - /// Current runtime heartbeat snapshot. + /// public WorkerRuntimeHeartbeatSnapshot CaptureHeartbeat() { lock (gate) @@ -122,9 +115,7 @@ internal sealed class FakeRuntimeSession : IWorkerRuntimeSession } } - /// Drains queued events up to the specified limit. - /// Maximum events to drain; 0 drains all. - /// The drained events. + /// public IReadOnlyList DrainEvents(uint maxEvents) { lock (gate) @@ -142,8 +133,7 @@ internal sealed class FakeRuntimeSession : IWorkerRuntimeSession } } - /// Drains a pending fault if any. - /// Pending fault or null. + /// public WorkerFault? DrainFault() { return null; @@ -195,9 +185,7 @@ internal sealed class FakeRuntimeSession : IWorkerRuntimeSession } } - /// Cancels command by correlation ID. - /// The command correlation ID. - /// True if cancelled; false otherwise. + /// public bool CancelCommand(string correlationId) { lock (gate) @@ -207,16 +195,13 @@ internal sealed class FakeRuntimeSession : IWorkerRuntimeSession } } - /// Requests graceful shutdown. + /// public void RequestShutdown() { releaseDispatch.Set(); } - /// Shuts down gracefully within the specified timeout. - /// Shutdown timeout period. - /// Cancellation token. - /// Shutdown result. + /// public Task ShutdownGracefullyAsync( TimeSpan timeout, CancellationToken cancellationToken = default) @@ -257,7 +242,7 @@ internal sealed class FakeRuntimeSession : IWorkerRuntimeSession } } - /// Disposes resources. + /// public void Dispose() { Disposed = true; diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/TestSupport/NoopMxAccessServer.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/TestSupport/NoopMxAccessServer.cs index 1754ed2..9682fac 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/TestSupport/NoopMxAccessServer.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/TestSupport/NoopMxAccessServer.cs @@ -46,20 +46,30 @@ internal sealed class NoopMxAccessServer : IMxAccessServer { } - /// + /// Adds a buffered item and returns an item handle; no-op for testing. + /// Server handle the item belongs to. + /// Item reference definition. + /// Caller-supplied item context. + /// Always 0 for testing. public int AddBufferedItem(int serverHandle, string itemDefinition, string itemContext) => 0; - /// + /// Sets the buffered update interval; no-op for testing. + /// Server handle the interval applies to. + /// Update interval in milliseconds. public void SetBufferedUpdateInterval(int serverHandle, int updateIntervalMilliseconds) { } - /// + /// Suspends updates for an item; no-op for testing. + /// Server handle the item belongs to. + /// Item handle to suspend. public void Suspend(int serverHandle, int itemHandle) { } - /// + /// Activates updates for an item; no-op for testing. + /// Server handle the item belongs to. + /// Item handle to activate. public void Activate(int serverHandle, int itemHandle) { } @@ -84,9 +94,14 @@ internal sealed class NoopMxAccessServer : IMxAccessServer { } - /// + /// Authenticates a user by name and password; always returns 0 for testing. + /// User name to authenticate. + /// User password. + /// Always 0 for testing. public int AuthenticateUser(string userName, string password) => 0; - /// + /// Resolves an ArchestrA user name to an identifier; always returns 0 for testing. + /// User name to resolve. + /// Always 0 for testing. public int ArchestrAUserToId(string userName) => 0; } diff --git a/src/ZB.MOM.WW.MxGateway.Worker.Tests/TestSupport/WorkerFrameTestHelpers.cs b/src/ZB.MOM.WW.MxGateway.Worker.Tests/TestSupport/WorkerFrameTestHelpers.cs index 4a78baf..d9d633b 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker.Tests/TestSupport/WorkerFrameTestHelpers.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker.Tests/TestSupport/WorkerFrameTestHelpers.cs @@ -12,6 +12,7 @@ internal static class WorkerFrameTestHelpers { /// Builds a length-prefixed frame from a protobuf message. /// Message to serialize into the frame payload. + /// A byte array containing the length-prefixed frame. public static byte[] CreateFrame(IMessage message) { return CreateFrame(message.ToByteArray()); @@ -19,6 +20,7 @@ internal static class WorkerFrameTestHelpers /// Builds a length-prefixed frame from a raw payload. /// Payload bytes to wrap in a frame. + /// A byte array containing the length-prefixed frame. public static byte[] CreateFrame(byte[] payload) { byte[] frame = new byte[sizeof(uint) + payload.Length]; diff --git a/src/ZB.MOM.WW.MxGateway.Worker/Bootstrap/WorkerConsoleLogger.cs b/src/ZB.MOM.WW.MxGateway.Worker/Bootstrap/WorkerConsoleLogger.cs index 68c98d4..534d72d 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/Bootstrap/WorkerConsoleLogger.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/Bootstrap/WorkerConsoleLogger.cs @@ -16,17 +16,13 @@ public sealed class WorkerConsoleLogger : IWorkerLogger _writer = writer ?? throw new ArgumentNullException(nameof(writer)); } - /// Writes an informational log entry. - /// Name of the event being logged. - /// Event fields and values to log. + /// public void Information(string eventName, IReadOnlyDictionary fields) { Write("Information", eventName, fields); } - /// Writes an error log entry. - /// Name of the event being logged. - /// Event fields and values to log. + /// public void Error(string eventName, IReadOnlyDictionary fields) { Write("Error", eventName, fields); diff --git a/src/ZB.MOM.WW.MxGateway.Worker/Bootstrap/WorkerLogRedactor.cs b/src/ZB.MOM.WW.MxGateway.Worker/Bootstrap/WorkerLogRedactor.cs index 25152aa..948cfaf 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/Bootstrap/WorkerLogRedactor.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/Bootstrap/WorkerLogRedactor.cs @@ -28,6 +28,7 @@ public static class WorkerLogRedactor /// Redacts sensitive field values from a log field dictionary. /// /// Dictionary of field names and values. + /// A new dictionary with sensitive values replaced by the redaction marker. public static Dictionary RedactFields(IReadOnlyDictionary fields) { Dictionary redactedFields = []; @@ -45,6 +46,7 @@ public static class WorkerLogRedactor /// /// Name of the field to check. /// Value to redact if sensitive. + /// The original value if not sensitive; otherwise . public static object? RedactValue(string fieldName, object? value) { if (value is null) diff --git a/src/ZB.MOM.WW.MxGateway.Worker/Conversion/MxStatusProxyConverter.cs b/src/ZB.MOM.WW.MxGateway.Worker/Conversion/MxStatusProxyConverter.cs index aad07a4..00a97ca 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/Conversion/MxStatusProxyConverter.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/Conversion/MxStatusProxyConverter.cs @@ -11,6 +11,7 @@ public sealed class MxStatusProxyConverter { /// Converts a single status object to a protobuf message, reflecting all fields and diagnostics. /// COM status object to convert. + /// The converted protobuf status message. public MxStatusProxy Convert(object status) { if (status is null) @@ -38,6 +39,7 @@ public sealed class MxStatusProxyConverter /// Converts an array of status objects, handling nulls gracefully. /// Array of COM status objects; null returns empty list. + /// A read-only list of converted status messages. public IReadOnlyList ConvertMany(Array? statuses) { if (statuses is null) @@ -67,6 +69,7 @@ public sealed class MxStatusProxyConverter /// Preserves completion-only status bytes as a diagnostic hex string since they cannot be unpacked. /// Status bytes to encode as hex string. + /// A hex-encoded diagnostic string. public string PreserveCompletionOnlyStatusBytes(byte[] statusBytes) { if (statusBytes is null) diff --git a/src/ZB.MOM.WW.MxGateway.Worker/Ipc/IWorkerPipeClient.cs b/src/ZB.MOM.WW.MxGateway.Worker/Ipc/IWorkerPipeClient.cs index 37bad96..c42b21d 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/Ipc/IWorkerPipeClient.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/Ipc/IWorkerPipeClient.cs @@ -10,6 +10,7 @@ public interface IWorkerPipeClient /// Connects to the gateway and runs the worker until the session ends or is cancelled. /// Configuration options. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. Task RunAsync( WorkerOptions options, CancellationToken cancellationToken = default); diff --git a/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerFrameReader.cs b/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerFrameReader.cs index 3ff27a0..781c6a8 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerFrameReader.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerFrameReader.cs @@ -27,6 +27,7 @@ public sealed class WorkerFrameReader /// Reads and validates a single length-prefixed frame from the stream. /// Token to cancel the asynchronous operation. + /// A task that resolves to the parsed . public async Task ReadAsync(CancellationToken cancellationToken = default) { byte[] lengthPrefix = new byte[sizeof(uint)]; diff --git a/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerFrameWriter.cs b/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerFrameWriter.cs index 478f6eb..a80d548 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerFrameWriter.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerFrameWriter.cs @@ -28,6 +28,7 @@ public sealed class WorkerFrameWriter /// Writes a worker envelope frame to the stream with length prefix. /// Worker envelope to write. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. public async Task WriteAsync( WorkerEnvelope envelope, CancellationToken cancellationToken = default) diff --git a/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerPipeClient.cs b/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerPipeClient.cs index bfba1be..ab47518 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerPipeClient.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerPipeClient.cs @@ -139,11 +139,7 @@ public sealed class WorkerPipeClient : IWorkerPipeClient _connectAttemptTimeoutMilliseconds = connectAttemptTimeoutMilliseconds; } - /// - /// Runs the worker by connecting to the gateway and executing the frame protocol. - /// - /// Worker configuration options. - /// Token to cancel the asynchronous operation. + /// public async Task RunAsync( WorkerOptions options, CancellationToken cancellationToken = default) diff --git a/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerPipeSession.cs b/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerPipeSession.cs index fa9f297..903eff8 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerPipeSession.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/Ipc/WorkerPipeSession.cs @@ -106,6 +106,7 @@ public sealed class WorkerPipeSession /// Runs the worker session, completing the handshake and processing messages until cancellation. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. public async Task RunAsync(CancellationToken cancellationToken = default) { // Worker-025: the factory delegate itself is null-checked in the @@ -142,6 +143,7 @@ public sealed class WorkerPipeSession /// Completes the gateway startup handshake using default MXAccess initialization. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. public Task CompleteStartupHandshakeAsync(CancellationToken cancellationToken = default) { return CompleteStartupHandshakeAsync(InitializeMxAccessAsync, cancellationToken); @@ -150,6 +152,7 @@ public sealed class WorkerPipeSession /// Completes the gateway startup handshake with custom MXAccess initialization that returns void. /// Async function to initialize MXAccess. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. public async Task CompleteStartupHandshakeAsync( Func initializeMxAccessAsync, CancellationToken cancellationToken = default) @@ -171,6 +174,7 @@ public sealed class WorkerPipeSession /// Completes the gateway startup handshake with custom MXAccess initialization that returns WorkerReady. /// Async function to initialize MXAccess and return ready state. /// Token to cancel the asynchronous operation. + /// A task that represents the asynchronous operation. public async Task CompleteStartupHandshakeAsync( Func> initializeMxAccessAsync, CancellationToken cancellationToken = default) diff --git a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AlarmDispatcher.cs b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AlarmDispatcher.cs index 7a9bce9..9df083f 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AlarmDispatcher.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AlarmDispatcher.cs @@ -154,6 +154,7 @@ public sealed class AlarmDispatcher : IDisposable /// protos for the /// QueryActiveAlarms RPC's ConditionRefresh stream. /// + /// The currently-active alarms as protos. public IReadOnlyList SnapshotActiveAlarms() { if (disposed) throw new ObjectDisposedException(nameof(AlarmDispatcher)); diff --git a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AlarmRecordTransitionMapper.cs b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AlarmRecordTransitionMapper.cs index fefa0c9..ff6b138 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AlarmRecordTransitionMapper.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/AlarmRecordTransitionMapper.cs @@ -24,6 +24,7 @@ public static class AlarmRecordTransitionMapper /// . /// /// The state XML string from AVEVA (e.g., UNACK_ALM, ACK_RTN). + /// The corresponding , or for unknown values. public static MxAlarmStateKind ParseStateKind(string? stateXml) { if (string.IsNullOrWhiteSpace(stateXml)) return MxAlarmStateKind.Unspecified; @@ -51,6 +52,7 @@ public static class AlarmRecordTransitionMapper /// /// The previous alarm state kind. /// The current alarm state kind. + /// The proto representing the state change, or when no mapped kind applies. public static AlarmTransitionKind MapTransition( MxAlarmStateKind previous, MxAlarmStateKind current) @@ -87,6 +89,7 @@ public static class AlarmRecordTransitionMapper /// The provider name, or null. /// The group name, or null. /// The alarm name, or null. + /// The composed full reference string in Provider!Group.AlarmName format. public static string ComposeFullReference(string? providerName, string? groupName, string? alarmName) { string provider = providerName ?? string.Empty; @@ -113,6 +116,7 @@ public static class AlarmRecordTransitionMapper /// e.g. "13:26:14.709". /// Offset of the producer's local time vs UTC, in minutes. /// DST adjustment already applied to local time, in minutes. + /// The UTC , or when the date or time cannot be parsed. public static DateTime ParseTransitionTimestampUtc( string? xmlDate, string? xmlTime, diff --git a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/IAlarmCommandHandler.cs b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/IAlarmCommandHandler.cs index bad5680..6cc458e 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/IAlarmCommandHandler.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/IAlarmCommandHandler.cs @@ -28,6 +28,7 @@ public interface IAlarmCommandHandler : IDisposable /// The operator node name. /// The operator domain name. /// The operator full name. + /// AVEVA's native status code (0 = success). int Acknowledge( Guid alarmGuid, string comment, @@ -48,6 +49,7 @@ public interface IAlarmCommandHandler : IDisposable /// The operator node name. /// The operator domain name. /// The operator full name. + /// AVEVA's native status code (0 = success). int AcknowledgeByName( string alarmName, string providerName, @@ -63,6 +65,7 @@ public interface IAlarmCommandHandler : IDisposable /// prefix matched against AlarmFullReference. /// /// Optional prefix to filter alarms by. + /// The currently active alarms matching the filter, or all alarms when no filter is given. IReadOnlyList QueryActive(string? alarmFilterPrefix); /// diff --git a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/IMxAccessAlarmConsumer.cs b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/IMxAccessAlarmConsumer.cs index b3a18cf..4bfc1b0 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/IMxAccessAlarmConsumer.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/IMxAccessAlarmConsumer.cs @@ -101,6 +101,7 @@ public interface IMxAccessAlarmConsumer : IDisposable /// ConditionRefresh path — operator clients call this after reconnect /// to seed local Part 9 state. /// + /// The most recently parsed snapshot of active alarm records. IReadOnlyList SnapshotActiveAlarms(); /// diff --git a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/IWorkerRuntimeSession.cs b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/IWorkerRuntimeSession.cs index 0785e0b..da4317a 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/IWorkerRuntimeSession.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/IWorkerRuntimeSession.cs @@ -34,6 +34,7 @@ public interface IWorkerRuntimeSession : IDisposable /// /// Captures a heartbeat snapshot of the runtime state. /// + /// A snapshot of the current worker runtime heartbeat state. WorkerRuntimeHeartbeatSnapshot CaptureHeartbeat(); /// @@ -46,6 +47,7 @@ public interface IWorkerRuntimeSession : IDisposable /// /// Drains a pending fault from the queue, if any. /// + /// The pending fault, or null if no fault is queued. WorkerFault? DrainFault(); /// diff --git a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessCommandExecutor.cs b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessCommandExecutor.cs index 446be67..a94af69 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessCommandExecutor.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessCommandExecutor.cs @@ -82,11 +82,7 @@ public sealed class MxAccessCommandExecutor : IStaCommandExecutor this.pumpStep = pumpStep ?? (static () => { }); } - /// - /// Executes an MXAccess command and returns the reply. - /// - /// STA command to execute. - /// Command reply with result or error details. + /// public MxCommandReply Execute(StaCommand command) { if (command is null) diff --git a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessEventMapper.cs b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessEventMapper.cs index 3f3f40b..592bdec 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessEventMapper.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessEventMapper.cs @@ -37,6 +37,7 @@ public sealed class MxAccessEventMapper /// Item quality code from MXAccess. /// Item timestamp from MXAccess. /// Array of MxStatusProxy values from MXAccess. + /// The constructed for this data-change notification. public MxEvent CreateOnDataChange( string sessionId, int serverHandle, @@ -65,6 +66,7 @@ public sealed class MxAccessEventMapper /// Handle returned by the worker. /// Handle returned by the worker. /// Array of MxStatusProxy values from MXAccess. + /// The constructed for this write-complete notification. public MxEvent CreateOnWriteComplete( string sessionId, int serverHandle, @@ -87,6 +89,7 @@ public sealed class MxAccessEventMapper /// Handle returned by the worker. /// Handle returned by the worker. /// Array of MxStatusProxy values from MXAccess. + /// The constructed for this operation-complete notification. public MxEvent CreateOperationComplete( string sessionId, int serverHandle, @@ -124,6 +127,7 @@ public sealed class MxAccessEventMapper /// Alarm taxonomy bucket from the Galaxy template. /// Human-readable alarm description. /// Array of MxStatusProxy values from MXAccess. + /// The constructed for this alarm-transition notification. public MxEvent CreateOnAlarmTransition( string sessionId, string alarmFullReference, @@ -178,6 +182,7 @@ public sealed class MxAccessEventMapper /// Array of quality values from MXAccess. /// Array of timestamp values from MXAccess. /// Array of MxStatusProxy values from MXAccess. + /// The constructed for this buffered data-change notification. public MxEvent CreateOnBufferedDataChange( string sessionId, int serverHandle, diff --git a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessEventQueue.cs b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessEventQueue.cs index ae8393b..a126bfe 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessEventQueue.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessEventQueue.cs @@ -148,6 +148,7 @@ public sealed class MxAccessEventQueue /// Attempts to dequeue the next event without removing it if empty. /// /// The dequeued event if successful; null if queue is empty. + /// true if an event was dequeued; false if the queue was empty. public bool TryDequeue(out WorkerEvent? workerEvent) { lock (syncRoot) @@ -167,6 +168,7 @@ public sealed class MxAccessEventQueue /// Drains up to maxEvents from the queue; if maxEvents is 0, drains all events. /// /// Maximum number of events to drain; 0 means drain all. + /// The drained events in FIFO order. public IReadOnlyList Drain(uint maxEvents) { lock (syncRoot) @@ -209,6 +211,7 @@ public sealed class MxAccessEventQueue /// /// Returns and clears the fault so it is not reported twice. /// + /// The pending fault, or null if no fault has been recorded or it was already drained. public WorkerFault? DrainFault() { lock (syncRoot) diff --git a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessHandleRegistry.cs b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessHandleRegistry.cs index 1e8526f..f0d4588 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessHandleRegistry.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessHandleRegistry.cs @@ -66,6 +66,7 @@ public sealed class MxAccessHandleRegistry /// Checks if the registry contains the specified server handle. /// Handle returned by the worker. + /// true if the server handle is registered; otherwise false. public bool ContainsServerHandle(int serverHandle) { return serverHandles.ContainsKey(serverHandle); @@ -106,6 +107,7 @@ public sealed class MxAccessHandleRegistry /// Checks if the registry contains the specified item handle. /// Handle returned by the worker. /// Handle returned by the worker. + /// true if the item handle is registered; otherwise false. public bool ContainsItemHandle( int serverHandle, int itemHandle) @@ -149,6 +151,7 @@ public sealed class MxAccessHandleRegistry /// Handle returned by the worker. /// Handle returned by the worker. /// Type of advice to check. + /// true if the advice handle is registered; otherwise false. public bool ContainsAdviceHandle( int serverHandle, int itemHandle, diff --git a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessSession.cs b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessSession.cs index c89c667..99d0a52 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessSession.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessSession.cs @@ -47,6 +47,7 @@ public sealed class MxAccessSession : IDisposable /// Creates a WorkerReady message with session metadata. /// Process ID of the worker. + /// A populated protobuf message. public WorkerReady CreateWorkerReady(int workerProcessId) { return new WorkerReady @@ -77,6 +78,7 @@ public sealed class MxAccessSession : IDisposable /// Optional handle registry; a fresh one is created when null. /// Optional value cache; a fresh one is created when null. /// Optional creation thread id; defaults to the current managed thread id. + /// A new wired to the supplied test doubles. /// /// Thrown when is the production /// . Tests must pass a test @@ -110,6 +112,7 @@ public sealed class MxAccessSession : IDisposable /// Factory to create the MXAccess COM object. /// Event sink to attach to the COM object. /// Identifier of the session. + /// A fully initialized ready for use. public static MxAccessSession Create( IMxAccessComObjectFactory factory, IMxAccessEventSink eventSink, @@ -176,6 +179,7 @@ public sealed class MxAccessSession : IDisposable /// Registers a client with MXAccess and returns the server handle. /// Name of the client to register. + /// The MXAccess server handle for the registered client. public int Register(string clientName) { ThrowIfDisposed(); @@ -199,6 +203,7 @@ public sealed class MxAccessSession : IDisposable /// Adds an item to an MXAccess server and returns the item handle. /// Handle returned by the worker. /// Definition or address of the item to add. + /// The MXAccess item handle for the added item. public int AddItem( int serverHandle, string itemDefinition) @@ -220,6 +225,7 @@ public sealed class MxAccessSession : IDisposable /// Handle returned by the worker. /// Definition or address of the item to add. /// Context string for the item. + /// The MXAccess item handle for the added item. public int AddItem2( int serverHandle, string itemDefinition, @@ -375,6 +381,7 @@ public sealed class MxAccessSession : IDisposable /// Adds multiple items in bulk, returning success/failure results. /// Handle returned by the worker. /// Enumerable of item definitions to add. + /// Per-item success or failure results in the same order as . public IReadOnlyList AddItemBulk( int serverHandle, IEnumerable tagAddresses) @@ -411,6 +418,7 @@ public sealed class MxAccessSession : IDisposable /// Advises on multiple items in bulk, returning success/failure results. /// Handle returned by the worker. /// Enumerable of item handles to advise on. + /// Per-item success or failure results in the same order as . public IReadOnlyList AdviseItemBulk( int serverHandle, IEnumerable itemHandles) @@ -441,6 +449,7 @@ public sealed class MxAccessSession : IDisposable /// Removes multiple items in bulk, returning success/failure results. /// Handle returned by the worker. /// Enumerable of item handles to remove. + /// Per-item success or failure results in the same order as . public IReadOnlyList RemoveItemBulk( int serverHandle, IEnumerable itemHandles) @@ -471,6 +480,7 @@ public sealed class MxAccessSession : IDisposable /// Removes advice subscriptions from multiple items in bulk, returning success/failure results. /// Handle returned by the worker. /// Enumerable of item handles to unadvise. + /// Per-item success or failure results in the same order as . public IReadOnlyList UnAdviseItemBulk( int serverHandle, IEnumerable itemHandles) @@ -501,6 +511,7 @@ public sealed class MxAccessSession : IDisposable /// Adds multiple items and subscribes to them in bulk, returning success/failure results. /// Handle returned by the worker. /// Enumerable of item definitions to add and subscribe to. + /// Per-item success or failure results in the same order as . public IReadOnlyList SubscribeBulk( int serverHandle, IEnumerable tagAddresses) @@ -545,6 +556,7 @@ public sealed class MxAccessSession : IDisposable /// Unsubscribes from multiple items in bulk, returning success/failure results. /// Handle returned by the worker. /// Enumerable of item handles to unsubscribe from. + /// Per-item success or failure results in the same order as . public IReadOnlyList UnsubscribeBulk( int serverHandle, IEnumerable itemHandles) @@ -596,6 +608,7 @@ public sealed class MxAccessSession : IDisposable /// The MXAccess server handle. /// The write entries to process. /// Converts protobuf MxValue to COM-compatible variant. + /// Per-entry write results; failures are captured rather than thrown. public IReadOnlyList WriteBulk( int serverHandle, IReadOnlyList entries, @@ -628,6 +641,7 @@ public sealed class MxAccessSession : IDisposable /// The MXAccess server handle. /// The write2 entries to process. /// Converts protobuf MxValue to COM-compatible variant. + /// Per-entry write results; failures are captured rather than thrown. public IReadOnlyList Write2Bulk( int serverHandle, IReadOnlyList entries, @@ -665,6 +679,7 @@ public sealed class MxAccessSession : IDisposable /// The MXAccess server handle. /// The WriteSecured entries to process. /// Converts protobuf MxValue to COM-compatible variant. + /// Per-entry write results; failures are captured rather than thrown. public IReadOnlyList WriteSecuredBulk( int serverHandle, IReadOnlyList entries, @@ -702,6 +717,7 @@ public sealed class MxAccessSession : IDisposable /// The MXAccess server handle. /// The WriteSecured2 entries to process. /// Converts protobuf MxValue to COM-compatible variant. + /// Per-entry write results; failures are captured rather than thrown. public IReadOnlyList WriteSecured2Bulk( int serverHandle, IReadOnlyList entries, @@ -750,6 +766,7 @@ public sealed class MxAccessSession : IDisposable /// The tag addresses to read. /// The timeout per tag. /// Action invoked on each poll iteration. + /// Per-tag read results including value, quality, and status. public IReadOnlyList ReadBulk( int serverHandle, IReadOnlyList tagAddresses, @@ -991,6 +1008,7 @@ public sealed class MxAccessSession : IDisposable } /// Gracefully shuts down the session, cleaning up all handles. + /// A result containing any cleanup failures encountered during shutdown. public MxAccessShutdownResult ShutdownGracefully() { if (disposed) @@ -1008,7 +1026,7 @@ public sealed class MxAccessSession : IDisposable return new MxAccessShutdownResult(failures); } - /// Releases the MXAccess COM object and resources. + /// public void Dispose() { if (disposed) diff --git a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessStaSession.cs b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessStaSession.cs index cd8448b..49f23cf 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessStaSession.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessStaSession.cs @@ -168,13 +168,7 @@ public sealed class MxAccessStaSession : IWorkerRuntimeSession return StartAsync(string.Empty, workerProcessId, cancellationToken); } - /// - /// Starts the MXAccess COM session with a session ID asynchronously. - /// - /// Session identifier. - /// Worker process identifier. - /// Cancellation token. - /// Worker ready message. + /// public async Task StartAsync( string sessionId, int workerProcessId, @@ -368,11 +362,7 @@ public sealed class MxAccessStaSession : IWorkerRuntimeSession return fault; } - /// - /// Dispatches a command to the STA thread for execution asynchronously. - /// - /// The command to dispatch. - /// Command reply. + /// public Task DispatchAsync(StaCommand command) { if (commandDispatcher is null) @@ -383,10 +373,7 @@ public sealed class MxAccessStaSession : IWorkerRuntimeSession return commandDispatcher.DispatchAsync(command); } - /// - /// Captures a heartbeat snapshot of the session's runtime state. - /// - /// Heartbeat snapshot. + /// public WorkerRuntimeHeartbeatSnapshot CaptureHeartbeat() { uint pendingCommandCount = 0; @@ -406,38 +393,25 @@ public sealed class MxAccessStaSession : IWorkerRuntimeSession currentCommandCorrelationId); } - /// - /// Requests graceful shutdown of the command dispatcher. - /// + /// public void RequestShutdown() { commandDispatcher?.RequestShutdown(); } - /// - /// Drains up to the specified number of events from the queue. - /// - /// Maximum events to drain. - /// Drained events. + /// public IReadOnlyList DrainEvents(uint maxEvents) { return eventQueue.Drain(maxEvents); } - /// - /// Drains a fault from the queue if present. - /// - /// Drained fault or null. + /// public WorkerFault? DrainFault() { return eventQueue.DrainFault(); } - /// - /// Cancels a queued command by correlation ID. - /// - /// Correlation ID of the command to cancel. - /// True if cancelled; otherwise false. + /// public bool CancelCommand(string correlationId) { return commandDispatcher?.CancelQueuedCommand(correlationId) ?? false; @@ -497,12 +471,7 @@ public sealed class MxAccessStaSession : IWorkerRuntimeSession cancellationToken); } - /// - /// Performs graceful shutdown of the MXAccess session within a timeout. - /// - /// Maximum time allowed for shutdown. - /// Cancellation token. - /// Shutdown result with any cleanup failures. + /// public async Task ShutdownGracefullyAsync( TimeSpan timeout, CancellationToken cancellationToken = default) @@ -610,7 +579,7 @@ public sealed class MxAccessStaSession : IWorkerRuntimeSession return result; } - /// Releases resources and shuts down the session. + /// public void Dispose() { if (disposed) diff --git a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessValueCache.cs b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessValueCache.cs index 7bba593..bc9b366 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessValueCache.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/MxAccessValueCache.cs @@ -60,6 +60,7 @@ public sealed class MxAccessValueCache /// MXAccess server handle. /// MXAccess item handle. /// The cached value if found. + /// True if a cached entry exists; otherwise false. public bool TryGet( int serverHandle, int itemHandle, @@ -102,6 +103,7 @@ public sealed class MxAccessValueCache /// Action that pumps any pending Windows messages. /// The cached value if the update was received before the deadline. /// How long to sleep between pump cycles. Default 5 ms. + /// True if a new value arrived before the deadline; false if the deadline elapsed. public bool TryWaitForUpdate( int serverHandle, int itemHandle, @@ -137,6 +139,7 @@ public sealed class MxAccessValueCache /// Returns the current version for a handle pair, or 0 if no entry exists. /// MXAccess server handle. /// MXAccess item handle. + /// The current version counter, or 0 if no entry is cached. public ulong CurrentVersion( int serverHandle, int itemHandle) diff --git a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/WnWrapAlarmConsumer.cs b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/WnWrapAlarmConsumer.cs index 250cc97..77015b2 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/WnWrapAlarmConsumer.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/MxAccess/WnWrapAlarmConsumer.cs @@ -86,7 +86,7 @@ public sealed class WnWrapAlarmConsumer : IMxAccessAlarmConsumer : DefaultMaxAlarmsPerFetch; } - /// + /// Raised when a new alarm transition is detected during a poll cycle. public event EventHandler? AlarmTransitionEmitted; /// @@ -297,15 +297,7 @@ public sealed class WnWrapAlarmConsumer : IMxAccessAlarmConsumer } } - /// - /// Synchronously poll the wnwrap consumer once and dispatch any - /// transitions. STA-bound hosts drive polling by calling this from - /// the thread that owns the COM object. The consumer deliberately - /// owns no internal timer: a thread-pool timer would call the - /// apartment-threaded COM object off its owning STA and can block - /// indefinitely on cross-apartment marshaling when the STA is not - /// pumping messages. - /// + /// public void PollOnce() { wwAlarmConsumerClass? com; @@ -396,6 +388,7 @@ public sealed class WnWrapAlarmConsumer : IMxAccessAlarmConsumer /// resync). /// /// The XML snapshot payload. + /// A GUID-keyed dictionary of snapshot alarm records. public static Dictionary ParseSnapshotXml(string xml) { Dictionary records = @@ -460,6 +453,7 @@ public sealed class WnWrapAlarmConsumer : IMxAccessAlarmConsumer /// /// The 32-character hex GUID string. /// The parsed GUID, or Empty if parsing fails. + /// true if parsing succeeded; otherwise false. public static bool TryParseHexGuid(string? hex, out Guid guid) { guid = Guid.Empty; @@ -487,6 +481,7 @@ public sealed class WnWrapAlarmConsumer : IMxAccessAlarmConsumer /// so the worker doesn't fail to start. /// /// The subscription expression. + /// The XML alarm query string for SetXmlAlarmQuery. internal static string ComposeXmlAlarmQuery(string subscription) { string node = Environment.MachineName; diff --git a/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaComApartmentInitializer.cs b/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaComApartmentInitializer.cs index ca332db..3d40b4b 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaComApartmentInitializer.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaComApartmentInitializer.cs @@ -9,7 +9,7 @@ public sealed class StaComApartmentInitializer : IStaComApartmentInitializer private const int SOk = 0; private const int SFalse = 1; - /// Initializes the COM apartment in single-threaded mode. + /// public void Initialize() { int hresult = CoInitializeEx(IntPtr.Zero, CoInitializeApartmentThreaded); @@ -19,7 +19,7 @@ public sealed class StaComApartmentInitializer : IStaComApartmentInitializer } } - /// Uninitializes the COM apartment. + /// public void Uninitialize() { CoUninitialize(); diff --git a/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaMessagePump.cs b/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaMessagePump.cs index 3284aa3..685a16c 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaMessagePump.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaMessagePump.cs @@ -43,6 +43,7 @@ public sealed class StaMessagePump } /// Pumps and dispatches all pending Windows messages, returning the count processed. + /// The number of Windows messages dispatched. public int PumpPendingMessages() { int pumpedMessages = 0; diff --git a/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaRuntime.cs b/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaRuntime.cs index 3f90b7f..98793b3 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaRuntime.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaRuntime.cs @@ -86,6 +86,7 @@ public sealed class StaRuntime : IDisposable /// wait. Callers must already be on the STA; the method is otherwise /// safe (PeekMessage simply finds no messages). /// + /// The number of Windows messages dispatched. public int PumpPendingMessages() => messagePump.PumpPendingMessages(); /// @@ -212,9 +213,7 @@ public sealed class StaRuntime : IDisposable return stopped; } - /// - /// Releases resources used by the STA runtime. - /// + /// public void Dispose() { if (disposed) diff --git a/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaWorkItem.cs b/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaWorkItem.cs index e7d7504..8861236 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaWorkItem.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/Sta/StaWorkItem.cs @@ -41,7 +41,7 @@ internal sealed class StaWorkItem : IStaWorkItem private TaskCompletionSource Completion { get; } - /// Cancels the work item before execution begins. + /// public void CancelBeforeExecution() { if (Interlocked.CompareExchange(ref started, 1, 0) == 0) @@ -51,7 +51,7 @@ internal sealed class StaWorkItem : IStaWorkItem } } - /// Executes the work item command. + /// public void Execute() { if (Interlocked.CompareExchange(ref started, 1, 0) != 0) diff --git a/src/ZB.MOM.WW.MxGateway.Worker/WorkerApplication.cs b/src/ZB.MOM.WW.MxGateway.Worker/WorkerApplication.cs index d253d86..1573c7f 100644 --- a/src/ZB.MOM.WW.MxGateway.Worker/WorkerApplication.cs +++ b/src/ZB.MOM.WW.MxGateway.Worker/WorkerApplication.cs @@ -11,6 +11,7 @@ public static class WorkerApplication { /// Initializes and runs the worker with default environment and logging. /// Command-line arguments. + /// The worker exit code. public static int Run(string[] args) { return Run( @@ -23,6 +24,7 @@ public static class WorkerApplication /// Command-line arguments. /// Worker environment for resolving configuration. /// Worker logger for diagnostics. + /// The worker exit code. public static int Run( string[] args, IWorkerEnvironment environment, @@ -40,6 +42,7 @@ public static class WorkerApplication /// Worker environment for resolving configuration. /// Worker logger for diagnostics. /// Named pipe client for gateway communication. + /// The worker exit code. public static int Run( string[] args, IWorkerEnvironment environment,