docs: strip task-tracking IDs from code comments and fill missing XML docs
ci / nightly-windev (push) Has been skipped
ci / windows-x86 (push) Successful in 1m16s
ci / java (push) Successful in 2m5s
ci / portable (push) Failing after 5m9s

Tracking IDs are review bookkeeping that rots in shipped source; the rationale they tagged is kept. Comment-only change (CommentChecker 458 -> 16 false positives).
This commit is contained in:
Joseph Doherty
2026-10-07 15:34:32 -04:00
parent f2e0861709
commit f40409a688
115 changed files with 549 additions and 411 deletions
@@ -16,7 +16,7 @@ using AuthenticationOptions = ZB.MOM.WW.MxGateway.Server.Configuration.Authentic
namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard;
/// <summary>
/// Covers SEC-40: the alarm feed requires a real authenticated dashboard role, so the read-only
/// Covers the rule that the alarm feed requires a real authenticated dashboard role, so the read-only
/// environment bypasses (<c>Authentication:Mode=Disabled</c>, anonymous localhost) no longer
/// receive galaxy-wide alarm metadata.
/// </summary>
@@ -65,7 +65,7 @@ public sealed class AlarmFeedAuthorizationTests
/// <summary>
/// An authenticated Viewer — the ordinary LDAP-logged-in dashboard user — does satisfy the
/// alarm requirement: SEC-40 excludes the environment bypasses, not Viewers. Alarm metadata
/// alarm requirement: the rule excludes the environment bypasses, not Viewers. Alarm metadata
/// stays galaxy-wide for them because the monitor is gateway-central and carries no
/// per-session provenance to tag-scope by.
/// </summary>
@@ -11,7 +11,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard;
/// <summary>
/// Verifies that <see cref="AlarmsHubPublisher"/> honours
/// <c>MxGateway:Dashboard:ShowTagValues</c> (TST-16): the alarm value fields of
/// <c>MxGateway:Dashboard:ShowTagValues</c>: the alarm value fields of
/// both value-bearing payload arms are stripped from the copy broadcast to
/// browser clients when the flag is off, present when it is on, and the source
/// <see cref="AlarmFeedMessage"/> — shared with the gRPC <c>StreamAlarms</c>
@@ -286,22 +286,48 @@ public sealed class AlarmsHubPublisherTests
/// <summary>Gets the capturing client proxy shared by this fake.</summary>
public CapturingClientProxy GroupProxy { get; } = new();
/// <summary>Gets the shared capturing proxy for all clients.</summary>
public IClientProxy All => GroupProxy;
/// <summary>Returns the shared capturing proxy, ignoring the exclusions.</summary>
/// <param name="excludedConnectionIds">Connection ids to exclude.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy AllExcept(IReadOnlyList<string> excludedConnectionIds) => GroupProxy;
/// <summary>Returns the shared capturing proxy for one connection.</summary>
/// <param name="connectionId">The target connection id.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy Client(string connectionId) => GroupProxy;
/// <summary>Returns the shared capturing proxy for several connections.</summary>
/// <param name="connectionIds">The target connection ids.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy Clients(IReadOnlyList<string> connectionIds) => GroupProxy;
/// <summary>Returns the shared capturing proxy for one group.</summary>
/// <param name="groupName">The target group name.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy Group(string groupName) => GroupProxy;
/// <summary>Returns the shared capturing proxy for a group, ignoring the exclusions.</summary>
/// <param name="groupName">The target group name.</param>
/// <param name="excludedConnectionIds">Connection ids to exclude.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy GroupExcept(string groupName, IReadOnlyList<string> excludedConnectionIds) => GroupProxy;
/// <summary>Returns the shared capturing proxy for several groups.</summary>
/// <param name="groupNames">The target group names.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy Groups(IReadOnlyList<string> groupNames) => GroupProxy;
/// <summary>Returns the shared capturing proxy for one user.</summary>
/// <param name="userId">The target user id.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy User(string userId) => GroupProxy;
/// <summary>Returns the shared capturing proxy for several users.</summary>
/// <param name="userIds">The target user ids.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy Users(IReadOnlyList<string> userIds) => GroupProxy;
}
@@ -342,9 +368,19 @@ public sealed class AlarmsHubPublisherTests
private sealed class NoopGroupManager : IGroupManager
{
/// <summary>Ignores the membership change and completes synchronously.</summary>
/// <param name="connectionId">The connection being added.</param>
/// <param name="groupName">The group the connection joins.</param>
/// <param name="cancellationToken">Token to observe for cancellation.</param>
/// <returns>A completed task.</returns>
public Task AddToGroupAsync(string connectionId, string groupName, CancellationToken cancellationToken = default)
=> Task.CompletedTask;
/// <summary>Ignores the removal and completes synchronously.</summary>
/// <param name="connectionId">The connection being removed.</param>
/// <param name="groupName">The group the connection leaves.</param>
/// <param name="cancellationToken">Token to observe for cancellation.</param>
/// <returns>A completed task.</returns>
public Task RemoveFromGroupAsync(string connectionId, string groupName, CancellationToken cancellationToken = default)
=> Task.CompletedTask;
}
@@ -180,7 +180,7 @@ public sealed class DashboardBrowseAndAlarmModelTests
/// <summary>
/// An explicitly-degraded status whose mode is still Alarmmgr (the
/// <c>Degraded || Mode==Subtag</c> guard's second, independent branch) must map to the
/// amber badge, but not to the failover label: GWC-37 gives that combination a meaning —
/// amber badge, but not to the failover label: that combination has its own meaning —
/// the alarm manager is serving and the subtag standby behind it covers nothing — so
/// reporting "Subtag monitoring (degraded)" would name the wrong live provider.
/// </summary>
@@ -9,7 +9,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard;
/// <summary>
/// Verifies that <see cref="DashboardEventBroadcaster"/> honours
/// <c>MxGateway:Dashboard:ShowTagValues</c> (SEC-25): tag values are stripped
/// <c>MxGateway:Dashboard:ShowTagValues</c>: tag values are stripped
/// from the mirrored copy when the flag is off, present when it is on, and the
/// shared source event is never mutated. Also verifies the viewer gate — the
/// mirror does no work at all for a session nobody is watching.
@@ -325,22 +325,48 @@ public sealed class DashboardEventBroadcasterTests
/// <summary>Gets the capturing client proxy shared by this fake.</summary>
public CapturingClientProxy GroupProxy { get; } = new();
/// <summary>Gets the shared capturing proxy for all connections.</summary>
public IClientProxy All => GroupProxy;
/// <summary>Returns the shared capturing proxy, ignoring exclusions.</summary>
/// <param name="excludedConnectionIds">Ignored.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy AllExcept(IReadOnlyList<string> excludedConnectionIds) => GroupProxy;
/// <summary>Returns the shared capturing proxy for any connection.</summary>
/// <param name="connectionId">Ignored.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy Client(string connectionId) => GroupProxy;
/// <summary>Returns the shared capturing proxy for any connection set.</summary>
/// <param name="connectionIds">Ignored.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy Clients(IReadOnlyList<string> connectionIds) => GroupProxy;
/// <summary>Returns the shared capturing proxy for any group.</summary>
/// <param name="groupName">Ignored.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy Group(string groupName) => GroupProxy;
/// <summary>Returns the shared capturing proxy for any group, ignoring exclusions.</summary>
/// <param name="groupName">Ignored.</param>
/// <param name="excludedConnectionIds">Ignored.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy GroupExcept(string groupName, IReadOnlyList<string> excludedConnectionIds) => GroupProxy;
/// <summary>Returns the shared capturing proxy for any group set.</summary>
/// <param name="groupNames">Ignored.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy Groups(IReadOnlyList<string> groupNames) => GroupProxy;
/// <summary>Returns the shared capturing proxy for any user.</summary>
/// <param name="userId">Ignored.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy User(string userId) => GroupProxy;
/// <summary>Returns the shared capturing proxy for any user set.</summary>
/// <param name="userIds">Ignored.</param>
/// <returns>The shared capturing proxy.</returns>
public IClientProxy Users(IReadOnlyList<string> userIds) => GroupProxy;
}
@@ -367,9 +393,19 @@ public sealed class DashboardEventBroadcasterTests
private sealed class NoopGroupManager : IGroupManager
{
/// <summary>Ignores the group membership change.</summary>
/// <param name="connectionId">Ignored.</param>
/// <param name="groupName">Ignored.</param>
/// <param name="cancellationToken">Ignored.</param>
/// <returns>A completed task.</returns>
public Task AddToGroupAsync(string connectionId, string groupName, CancellationToken cancellationToken = default)
=> Task.CompletedTask;
/// <summary>Ignores the group membership change.</summary>
/// <param name="connectionId">Ignored.</param>
/// <param name="groupName">Ignored.</param>
/// <param name="cancellationToken">Ignored.</param>
/// <returns>A completed task.</returns>
public Task RemoveFromGroupAsync(string connectionId, string groupName, CancellationToken cancellationToken = default)
=> Task.CompletedTask;
}
@@ -20,6 +20,7 @@ public sealed class DashboardLiveDataServiceTests
/// <summary>
/// Verifies a tag already in the advise set is not subscribed again on a later read.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReadAsync_WhenTagAlreadySubscribed_DoesNotResubscribe()
{
@@ -41,6 +42,7 @@ public sealed class DashboardLiveDataServiceTests
/// Verifies the advise set is capped: subscribing past the cap unadvises the
/// least-recently-read tag on the worker and leaves the re-read tag advised.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReadAsync_PastCap_EvictsLeastRecentlyReadTag()
{
@@ -78,6 +80,7 @@ public sealed class DashboardLiveDataServiceTests
/// than the cap keeps them all (a read never evicts a tag it is about to return),
/// and the next read that subscribes anything squeezes the overshoot back out.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReadAsync_WithMoreDistinctTagsThanCap_KeepsThemAllThenSelfCorrects()
{
@@ -108,6 +111,7 @@ public sealed class DashboardLiveDataServiceTests
/// touches nearly the whole advise set evicts only the untouched remainder, ends over
/// the cap, and the following read trims it back.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReadAsync_WhenTouchedTagsFillTheCap_EvictsOnlyUntouchedTags()
{
@@ -135,6 +139,7 @@ public sealed class DashboardLiveDataServiceTests
/// Verifies a tag the worker failed to advise still occupies a slot but is evicted
/// without any unsubscribe command — there is no item handle to unadvise.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReadAsync_WhenAdviseFailed_EvictsTagWithoutUnsubscribing()
{
@@ -165,6 +170,7 @@ public sealed class DashboardLiveDataServiceTests
/// Verifies a failed unadvise of an evicted tag does not fail the read, and the
/// evicted tag is dropped from tracking anyway.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReadAsync_WhenEvictionUnsubscribeFails_StillCompletesRead()
{
@@ -190,10 +196,11 @@ public sealed class DashboardLiveDataServiceTests
/// <summary>
/// Verifies the <c>/browse</c> live-value seam honours
/// <c>MxGateway:Dashboard:ShowTagValues</c> (TST-16): with the flag off — the
/// <c>MxGateway:Dashboard:ShowTagValues</c>: with the flag off — the
/// default — the value text the page renders is the redaction placeholder,
/// never the formatted tag value.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReadAsync_WhenShowTagValuesFalse_RedactsValueTextButKeepsMetadata()
{
@@ -220,6 +227,7 @@ public sealed class DashboardLiveDataServiceTests
}
/// <summary>Verifies the formatted value is served when the flag is on.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReadAsync_WhenShowTagValuesTrue_ServesFormattedValue()
{
@@ -243,6 +251,7 @@ public sealed class DashboardLiveDataServiceTests
/// withheld, and its diagnostic still reaches the panel. Redacting it would
/// contradict the error the same row displays.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
public async Task ReadAsync_WhenReadFailedAndShowTagValuesFalse_LeavesPlaceholderUnredacted()
{
@@ -489,7 +498,8 @@ public sealed class DashboardLiveDataServiceTests
{
}
/// <inheritdoc />
/// <summary>Completes immediately because the fake holds no resources.</summary>
/// <returns>A completed value task.</returns>
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
private BulkSubscribeReply Subscribe(IEnumerable<string> tagAddresses)
@@ -10,7 +10,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard;
/// <summary>
/// Covers <see cref="DashboardSessionAcl"/>, the single decision both dashboard subscribe seams
/// consult (SEC-25 / TST-15).
/// consult.
/// </summary>
/// <remarks>
/// Every branch is asserted in its denying direction as well as its allowing one, because the
@@ -77,6 +77,7 @@ public sealed class DashboardSessionAclTests
}
/// <summary>A blank session id is denied without consulting anything.</summary>
/// <param name="sessionId">A blank or whitespace-only session id.</param>
[Theory]
[InlineData("")]
[InlineData(" ")]
@@ -146,8 +147,8 @@ public sealed class DashboardSessionAclTests
}
/// <summary>
/// A principal carrying no tag claims — the anonymous-localhost / empty-grant Viewer of
/// SEC-02 — sees a tagged session never, and an untagged one only when the operator opted
/// A principal carrying no tag claims — the anonymous-localhost / empty-grant Viewer —
/// sees a tagged session never, and an untagged one only when the operator opted
/// into <see cref="UntaggedSessionVisibility.AllViewers"/>.
/// </summary>
[Fact]
@@ -179,7 +180,7 @@ public sealed class DashboardSessionAclTests
}
/// <summary>
/// SEC-41, the load-bearing regression: a cookie-style principal whose tag claim was stamped
/// The load-bearing regression: a cookie-style principal whose tag claim was stamped
/// at login under an older <c>GroupToTag</c> map is DENIED once that map no longer grants the
/// tag to any group the principal belongs to. The auth cookie survives the restart that
/// rebinds options, so trusting the stamped claim let a config-revoked grant live on for the
@@ -224,7 +225,7 @@ public sealed class DashboardSessionAclTests
/// <summary>
/// Re-resolution is ordinal-ignore-case on both sides — the group key against the map (which
/// <see cref="DashboardGroupTagMapping"/> owns) and the resolved tag against the session's tag
/// set. Pinned because the pre-SEC-41 claim scan got the second comparison from
/// set. Pinned because the earlier claim scan got the second comparison from
/// <c>Session.Tags</c>'s own comparer, and the re-resolution arm probes the grant set in the
/// opposite direction.
/// </summary>
@@ -294,7 +295,7 @@ public sealed class DashboardSessionAclTests
}
/// <summary>
/// Builds a dashboard principal. <paramref name="groups"/> is the SEC-41 discriminator: a
/// Builds a dashboard principal. <paramref name="groups"/> is the discriminator: a
/// principal WITH group claims is re-resolved against the current map (the cookie seam), one
/// WITHOUT falls back to its stamped tag claims (the hub-token seam). Existing cases pass
/// tags only, so they continue to exercise the fallback arm exactly as before.
@@ -7,7 +7,7 @@ using ZB.MOM.WW.MxGateway.Server.Dashboard.Hubs;
namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard;
/// <summary>
/// Covers the ACL gate on <see cref="EventsHub.SubscribeSession"/> (SEC-25 / TST-15).
/// Covers the ACL gate on <see cref="EventsHub.SubscribeSession"/> .
/// </summary>
/// <remarks>
/// The denial assertions are the load-bearing ones — before the gate existed every caller was
@@ -131,7 +131,11 @@ public sealed class EventsHubTests
/// <summary>Gets the (connection id, group name) pairs added, in order.</summary>
public List<(string ConnectionId, string GroupName)> Added { get; } = [];
/// <inheritdoc />
/// <summary>Records the group membership and completes synchronously.</summary>
/// <param name="connectionId">The connection being added.</param>
/// <param name="groupName">The group the connection joins.</param>
/// <param name="cancellationToken">Token to observe for cancellation.</param>
/// <returns>A completed task.</returns>
public Task AddToGroupAsync(string connectionId, string groupName, CancellationToken cancellationToken = default)
{
Added.Add((connectionId, groupName));
@@ -139,7 +143,11 @@ public sealed class EventsHubTests
return Task.CompletedTask;
}
/// <inheritdoc />
/// <summary>Ignores the removal and completes synchronously.</summary>
/// <param name="connectionId">The connection being removed.</param>
/// <param name="groupName">The group the connection leaves.</param>
/// <param name="cancellationToken">Token to observe for cancellation.</param>
/// <returns>A completed task.</returns>
public Task RemoveFromGroupAsync(
string connectionId,
string groupName,
@@ -129,6 +129,8 @@ public sealed class EventsHubViewerRegistryTests
}
/// <summary>Blank connection or session ids are ignored rather than tracked.</summary>
/// <param name="connectionId">The hub connection id, possibly blank.</param>
/// <param name="sessionId">The session id, possibly blank.</param>
[Theory]
[InlineData("", "session-1")]
[InlineData(" ", "session-1")]
@@ -178,7 +178,7 @@ public sealed class HubTokenServiceTests
}
/// <summary>
/// The dashboard visibility grant (SEC-25) survives the mint/validate round-trip: tags are
/// The dashboard visibility grant survives the mint/validate round-trip: tags are
/// resolved from the caller's LDAP-group claims through <c>Dashboard:GroupToTag</c> at
/// <see cref="HubTokenService.Issue(ClaimsPrincipal)"/> and rehydrated as
/// <see cref="DashboardAuthenticationDefaults.DashboardTagClaimType"/> claims on the principal
@@ -16,7 +16,7 @@ using ZB.MOM.WW.MxGateway.Server.Dashboard.Hubs;
namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Dashboard;
/// <summary>
/// Covers the ACL gate on the session-details page's in-process subscribe seam (SEC-25 / TST-15).
/// Covers the ACL gate on the session-details page's in-process subscribe seam.
/// </summary>
/// <remarks>
/// <para>
@@ -214,7 +214,7 @@ public sealed class SessionDetailsPageEventAclTests
/// <inheritdoc />
public ChannelReader<MxEvent> Reader => _channel.Reader;
/// <inheritdoc />
/// <summary>Marks the subscription as released and completes its channel.</summary>
public void Dispose()
{
IsDisposed = true;
@@ -81,7 +81,7 @@ public sealed class GatewayApplicationTests
{
// Bind an ephemeral port (:0) — xUnit runs test collections in parallel, so any
// started-host test must avoid a fixed port to prevent a bind collision. The auth SQLite
// store path is isolated to a per-process temp file by TestHostEnvironmentInitializer (SEC-33).
// store path is isolated to a per-process temp file by TestHostEnvironmentInitializer.
await using WebApplication app = GatewayApplication.Build(["--urls=http://127.0.0.1:0"]);
await app.StartAsync();
try
@@ -266,7 +266,7 @@ public sealed class GatewayApplicationTests
{
// Bind an ephemeral port (:0) — xUnit runs test collections in parallel, so any
// WebApplication-building test must avoid a fixed port to prevent a bind collision. The auth
// store path is isolated by TestHostEnvironmentInitializer (SEC-33), so startup opens a
// store path is isolated by TestHostEnvironmentInitializer, so startup opens a
// writable store and the injected misconfiguration is what fails validation.
await using WebApplication app = GatewayApplication.Build(
[$"--{key}={value}", "--urls=http://127.0.0.1:0"]);
@@ -19,7 +19,7 @@ namespace ZB.MOM.WW.MxGateway.Tests.Gateway;
/// <summary>
/// End-to-end reconnect/replay tests through the real gRPC <c>StreamEvents</c> path via the
/// fake worker harness (TST-01, server half). A single subscriber detaches (cancel + await
/// fake worker harness (server half). A single subscriber detaches (cancel + await
/// the stream task so its lease is fully disposed), the session is retained by detach-grace
/// while the distributor pump keeps appending worker events to the replay ring, and a second
/// stream reconnects with <see cref="StreamEventsRequest.AfterWorkerSequence"/>. Covers both
@@ -233,7 +233,7 @@ public sealed class GatewayEndToEndReconnectReplayTests
}
/// <summary>
/// GWC-25 regression, end to end: when age eviction has emptied the replay ring, the
/// Regression, end to end: when age eviction has emptied the replay ring, the
/// <c>ReplayGap</c> sentinel carries the next deliverable sequence (highest observed + 1),
/// so a client applying the documented
/// <c>after_worker_sequence = oldest_available_sequence - 1</c> formula on its follow-up
@@ -305,7 +305,7 @@ public sealed class GatewayEndToEndReconnectReplayTests
Assert.NotNull(sentinel.ReplayGap);
Assert.Equal(staleCursor, sentinel.ReplayGap.RequestedAfterSequence);
// The whole point of GWC-25: the sentinel anchors the resume at the next sequence that
// The whole point of the fix: the sentinel anchors the resume at the next sequence that
// can still be delivered, not at 0.
Assert.Equal(highestSeen + 1, sentinel.ReplayGap.OldestAvailableSequence);
@@ -28,7 +28,7 @@ public sealed class GalaxyRepositoryHostWiringTests
/// Verifies the full production authz-filtering chain end-to-end.
/// Phase 1: an identity carrying no <c>BrowseSubtrees</c> pushed → <c>BrowseChildren</c>
/// returns both children (unconstrained). That, not an absent identity, is production's
/// unscoped case since GWC-38 — with <c>Authentication:Mode=ApiKey</c> an absent identity
/// unscoped case — with <c>Authentication:Mode=ApiKey</c> an absent identity
/// now denies, which the isolated provider test pins.
/// Phase 2: identity with <c>BrowseSubtrees = ["NonExistent"]</c>
/// pushed via <see cref="GatewayRequestIdentityAccessor"/> → the real
@@ -52,7 +52,7 @@ public sealed class MxAccessGatewayServiceTests
/// <summary>
/// Verifies OpenSession forwards the calling key's dashboard-visibility tags, so the
/// session's tags are derived from the owning API key rather than the wire request (SEC-25).
/// session's tags are derived from the owning API key rather than the wire request.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
@@ -41,7 +41,7 @@ public sealed class MxAccessGrpcMapperTests
/// <summary>
/// The command-only overload exists so <c>Invoke</c> does not deep-clone the whole request just to
/// overwrite and discard its command (GWC-29). It must still perform the one clone that keeps the
/// overwrite and discard its command. It must still perform the one clone that keeps the
/// worker-bound graph unaliased from the caller-owned gRPC command, and must produce the same
/// <see cref="WorkerCommand"/> as the request overload.
/// </summary>
@@ -145,7 +145,7 @@ public sealed class MxAccessGrpcMapperTests
}
/// <summary>
/// Verifies MapEvent transfers ownership of the inner MxEvent (GWC-07 / IPC-05): the
/// Verifies MapEvent transfers ownership of the inner MxEvent: the
/// returned reference is the same instance carried by the WorkerEvent, not a clone. The
/// WorkerEvent is discarded after mapping and the distributor pump is its single consumer,
/// so moving the inner event out is safe and avoids a per-event deep copy.
@@ -669,7 +669,7 @@ public sealed class GatewaySessionTests
}
/// <summary>
/// GWC-27: <see cref="GatewaySession.AttachInternalEventSubscriber"/> must refuse to
/// <see cref="GatewaySession.AttachInternalEventSubscriber"/> must refuse to
/// attach before the session is Ready. Without the gate the attach would construct and
/// start the distributor against a not-yet-Ready worker; the pump source throws
/// <c>SessionNotReady</c>, every subscriber is completed with that error, and the
@@ -720,7 +720,7 @@ public sealed class GatewaySessionTests
}
/// <summary>
/// GWC-34, the disposal-side twin of the GWC-27 premature-attach poison: an attach that
/// The disposal-side twin of the premature-attach poison: an attach that
/// passed its readiness gate and then lost the CPU until the session was torn down used to
/// find <c>_eventDistributor</c> null, read that as "not created yet" and build a
/// REPLACEMENT distributor. Its pump could never start (the started flag is a one-way latch)
@@ -843,7 +843,7 @@ public sealed class SessionEventDistributorTests
}
/// <summary>
/// GWC-25: when age eviction has emptied the replay ring, a resume behind the highest
/// When age eviction has emptied the replay ring, a resume behind the highest
/// observed sequence still reports a gap, and the reported oldest-available sequence is
/// the next sequence that can possibly be delivered (highest seen + 1) — never <c>0</c>,
/// which would make the client's documented <c>oldest - 1</c> resume formula wrap to
@@ -889,7 +889,7 @@ public sealed class SessionEventDistributorTests
}
/// <summary>
/// GWC-25: the same next-deliverable-sequence rule applies when retention is disabled
/// The same next-deliverable-sequence rule applies when retention is disabled
/// outright (replay capacity 0) — the ring is always empty, so a behind cursor must still
/// get a usable resume anchor rather than <c>0</c>.
/// </summary>
@@ -924,7 +924,7 @@ public sealed class SessionEventDistributorTests
}
/// <summary>
/// GWC-25 regression: a client that applies the documented
/// Regression: a client that applies the documented
/// <c>after_worker_sequence = oldest_available_sequence - 1</c> formula to an empty-ring
/// ReplayGap resumes with a live watermark equal to the highest observed sequence, so the
/// caller's live filter passes every subsequent event. Under the pre-fix <c>0</c> the
@@ -41,7 +41,7 @@ public sealed class SessionManagerTests
/// Verifies the pipe name stays short enough that its Unix-domain-socket path
/// (TMPDIR + "CoreFxPipe_" + name) fits the 104-byte macOS sun_path limit under the
/// default per-user TMPDIR (~49 chars), and keeps the pid + session-guid uniqueness
/// contract (NEXT-01).
/// contract.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
@@ -110,7 +110,7 @@ public sealed class SessionManagerTests
}
/// <summary>
/// Verifies a session inherits the owning API key's dashboard-visibility tags (SEC-25),
/// Verifies a session inherits the owning API key's dashboard-visibility tags,
/// compared ordinal-ignore-case so a differently cased grant still matches.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
@@ -24,7 +24,10 @@ public sealed class SessionWorkerClientFactoryFakeWorkerTests : IAsyncLifetime
private readonly List<IWorkerTaskLauncher> _launchers = [];
/// <inheritdoc />
/// <summary>
/// Completes immediately; no asynchronous setup is required before each test.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
public Task InitializeAsync() => Task.CompletedTask;
/// <summary>
@@ -36,10 +36,10 @@ public sealed class WorkerClientTests
/// <summary>
/// The <c>GatewayHello</c> carries the negotiated worker-frame maximum so the worker adopts the
/// configured limit instead of its own default (IPC-02); a regression to sending <c>0</c> means
/// configured limit instead of its own default; a regression to sending <c>0</c> means
/// "older gateway, use default" to the worker and would silently downgrade the negotiated limit.
/// The adoption half is asserted only in the Windows-only worker suite, so the gateway half is
/// pinned here, in the portable suite (TST-28). Both the default and an override are covered so
/// pinned here, in the portable suite. Both the default and an override are covered so
/// the assertion tracks configuration rather than a constant.
/// </summary>
/// <param name="maxMessageBytes">Configured worker-frame maximum to negotiate.</param>
@@ -164,7 +164,7 @@ public sealed class WorkerClientTests
TestTimeout,
CancellationToken.None);
// The timeout also emits a WorkerCancel for the abandoned correlation (PERF-07), which sits
// The timeout also emits a WorkerCancel for the abandoned correlation, which sits
// ahead of the second command on the FIFO pipe; skip it rather than mistaking it for the
// command this assertion is about.
WorkerEnvelope secondCommand = await ReadNextCommandAsync(pipePair, timedOutCommand.CorrelationId);
@@ -180,7 +180,7 @@ public sealed class WorkerClientTests
/// <summary>
/// A command timeout abandons the gateway-side wait, but the worker keeps the correlation on
/// its single STA queue and would still run it — so the gateway forwards a <c>WorkerCancel</c>
/// for the abandoned correlation id (PERF-07). Without it, a client that retries after a
/// for the abandoned correlation id. Without it, a client that retries after a
/// timeout stacks work the worker still intends to execute. Asserted on the wire because the
/// cancel is protocol behavior the worker depends on, not an internal detail.
/// </summary>
@@ -223,7 +223,7 @@ public sealed class WorkerClientTests
/// <summary>
/// Caller cancellation abandons the correlation exactly as a gateway timeout does — a client
/// deadline shorter than the gateway's command timeout, or a disconnect, is the common
/// abandonment path — so it forwards the same <c>WorkerCancel</c> (IPC-34). Without it a
/// abandonment path — so it forwards the same <c>WorkerCancel</c>. Without it a
/// retry storm of deadline-cancelled calls leaves the worker's single STA working through
/// commands nobody is waiting for while live callers queue behind them. Twin of
/// <see cref="InvokeAsync_WhenCommandTimesOut_SendsWorkerCancelForThatCorrelation"/>, driven
@@ -271,7 +271,7 @@ public sealed class WorkerClientTests
/// <summary>
/// The cancel is forwarded for a caller that abandoned its command, never for the client's own
/// teardown (IPC-34): once the stop token has fired the outbound channel is being completed and
/// teardown: once the stop token has fired the outbound channel is being completed and
/// every pending command is failed anyway, so a cancel would at best be pointless and at worst
/// a write racing teardown. Asserted on the wire — the guard in the cancellation arm is the
/// code-level expression of it, and the completed outbound channel is the second line of
@@ -330,8 +330,7 @@ public sealed class WorkerClientTests
/// A <c>WorkerFault</c> arriving in place of a handshake frame is the worker reporting a
/// structured startup failure — most often MXAccess COM creation — not a protocol problem, so
/// the gateway surfaces <see cref="WorkerClientErrorCode.WorkerFaulted"/> with the fault's
/// category and diagnostic rather than a generic protocol violation naming only the body type
/// (IPC-35). This is the window where the information matters most: first session open on a
/// category and diagnostic rather than a generic protocol violation naming only the body type. This is the window where the information matters most: first session open on a
/// freshly deployed host, where the message reaches the operator log and the client's
/// <c>OpenFailed</c> detail.
/// </summary>
@@ -368,8 +367,7 @@ public sealed class WorkerClientTests
/// <summary>
/// The envelope <c>sequence</c> is a monotonic per-sender counter (gateway.md), so the values
/// observed on the pipe must be strictly increasing in wire order. Stamping the sequence when
/// the envelope is constructed lets two concurrent invokes stamp 1 and 2 but enqueue 2 then 1
/// (GWC-28); stamping on the single-consumer write loop makes wire order and sequence order the
/// the envelope is constructed lets two concurrent invokes stamp 1 and 2 but enqueue 2 then 1; stamping on the single-consumer write loop makes wire order and sequence order the
/// same thing by construction.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
@@ -710,12 +708,12 @@ public sealed class WorkerClientTests
}
/// <summary>
/// GWC-32: disposing a still-<c>Ready</c> client while the worker is emitting events must be
/// Disposing a still-<c>Ready</c> client while the worker is emitting events must be
/// an ordinary teardown, not a fault. <c>DisposeAsync</c> completes the same three channels
/// <c>MarkClosed</c> and <c>SetFaulted</c> do, so it must enter a terminal state first;
/// otherwise the read loop — already inside <c>DispatchEnvelope</c> when the staging channel
/// is completed, or reading a stream that is torn down underneath it — misdiagnoses the
/// completed channel as the GWC-24 sustained-backpressure overflow and raises a spurious
/// completed channel as the sustained-backpressure overflow and raises a spurious
/// <c>ProtocolViolation</c> fault, a <c>worker-event-staging</c> overflow metric and a
/// redundant kill during an intentional dispose. The event channel is sized far above the
/// burst so a genuine overflow is impossible, and <c>DisposeAsync</c> awaits its background
@@ -726,7 +724,7 @@ public sealed class WorkerClientTests
public async Task DisposeAsync_WhileWorkerIsEmittingEvents_ClosesWithoutFaultingOrOverflowing()
{
// The burst is capped at EmittedEventCeiling and the staging channel holds 2 x capacity, so
// even if every emitted event were staged and none consumed the GWC-24 bound is unreachable:
// even if every emitted event were staged and none consumed the staging bound is unreachable:
// an overflow metric in this test can only be the dispose-race misdiagnosis.
const int capacity = 2048;
using GatewayMetrics metrics = new();
@@ -1007,13 +1005,13 @@ public sealed class WorkerClientTests
}
/// <summary>
/// GWC-24: the staging channel between the read loop and the event writer is bounded at
/// The staging channel between the read loop and the event writer is bounded at
/// 2 × <see cref="WorkerClientOptions.EventChannelCapacity"/>, so a consumer that drains
/// slower than the worker produces faults the client at the bound instead of growing
/// gateway memory silently. The full-mode timeout here is far longer than the test could
/// ever wait, which proves the fault came from the staging bound and not from the timed
/// write in <c>EnqueueWorkerEventAsync</c>. A command reply interleaved before the fault
/// must still complete: the read loop never blocks behind events (the GWC-04 guarantee).
/// must still complete: the read loop never blocks behind events (the read-loop guarantee).
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>
[Fact]
@@ -1094,7 +1092,7 @@ public sealed class WorkerClientTests
}
/// <summary>
/// GWC-24: the worker event queue-depth gauge counts staged *and* queued events, so a
/// The worker event queue-depth gauge counts staged *and* queued events, so a
/// backlog held in the staging channel is visible rather than invisible. Depth is
/// incremented at staging and decremented when the consumer reads, so the single counter
/// reports total undelivered events and returns to zero once drained.
@@ -1148,7 +1146,7 @@ public sealed class WorkerClientTests
}
/// <summary>
/// GWC-33: a faulted client can remain retained for diagnostics, but its abandoned event
/// A faulted client can remain retained for diagnostics, but its abandoned event
/// backlog must stop contributing to the gateway-wide queue-depth gauge as soon as the
/// client enters a terminal state rather than waiting for disposal.
/// </summary>
@@ -57,7 +57,7 @@ public sealed class WorkerFrameProtocolTests
/// <summary>
/// One reader instance reads many frames in sequence. The reader reuses a single length-prefix
/// scratch buffer across calls (GWC-30), so varying the payload length frame to frame proves the
/// scratch buffer across calls, so varying the payload length frame to frame proves the
/// reused buffer is fully overwritten each time rather than carrying a stale prefix forward.
/// </summary>
/// <returns>A task that represents the asynchronous operation.</returns>