docs(src): add missing XML docs and strip tracking-ID comments
Sweep of 203 source files resolving CommentChecker findings: add <summary>/<param>/<returns>/<inheritdoc> where missing, and remove resolved task/issue tracking markers (Tests-NNN, Worker-NNN, Server-NNN, Task N) from code comments. Comment/doc-only — no logic changes. Server+Tests build clean under TreatWarningsAsErrors.
This commit is contained in:
@@ -16,6 +16,8 @@ public sealed class SessionEventDistributorTests
|
||||
{
|
||||
private static readonly TimeSpan ReadTimeout = TimeSpan.FromSeconds(5);
|
||||
|
||||
/// <summary>Two subscribers registered on the same distributor both receive every fanned event, in order.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task TwoSubscribers_BothReceiveFannedEventsInOrder()
|
||||
{
|
||||
@@ -40,6 +42,8 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Equal(2ul, b2.WorkerSequence);
|
||||
}
|
||||
|
||||
/// <summary>Disposing one subscriber's lease stops delivery to it while the other subscriber keeps receiving events.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DisposingOneLease_StopsItsDelivery_OtherKeepsReceiving()
|
||||
{
|
||||
@@ -65,6 +69,8 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Equal(2ul, b2.WorkerSequence);
|
||||
}
|
||||
|
||||
/// <summary>A subscriber registered after the pump has started only receives events emitted after its registration.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task SubscriberRegisteredAfterStart_ReceivesEventsEmittedAfterRegistration()
|
||||
{
|
||||
@@ -84,6 +90,8 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Equal(2ul, b.WorkerSequence);
|
||||
}
|
||||
|
||||
/// <summary>Disposing the distributor completes every subscriber channel and stops the pump.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task DisposingDistributor_CompletesAllSubscriberChannels_AndStopsPump()
|
||||
{
|
||||
@@ -101,6 +109,8 @@ public sealed class SessionEventDistributorTests
|
||||
await AssertCompletedAsync(leaseB.Reader);
|
||||
}
|
||||
|
||||
/// <summary>Calling <c>Register</c> after the distributor has been disposed throws <see cref="ObjectDisposedException"/>.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task Register_AfterDispose_ThrowsObjectDisposedException()
|
||||
{
|
||||
@@ -113,6 +123,11 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Throws<ObjectDisposedException>(() => distributor.Register());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pins the nested-lock disposal behavior in <c>RegisterWithReplay</c>: calling it after the
|
||||
/// distributor has been disposed throws <see cref="ObjectDisposedException"/>.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task RegisterWithReplay_AfterDispose_ThrowsObjectDisposedException()
|
||||
{
|
||||
@@ -134,6 +149,8 @@ public sealed class SessionEventDistributorTests
|
||||
out _));
|
||||
}
|
||||
|
||||
/// <summary>When retained events exceed the replay buffer's capacity, the oldest entries are evicted first and the replay reports a gap.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task ReplayBuffer_OverCapacity_EvictsOldestFirst_AndReportsGap()
|
||||
{
|
||||
@@ -168,6 +185,8 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Equal(new ulong[] { 3, 4, 5 }, replay.Select(e => e.WorkerSequence));
|
||||
}
|
||||
|
||||
/// <summary>Requesting replay from a sequence still inside the retained window returns only the newer events, with no gap.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task ReplayBuffer_WithinRetainedWindow_ReturnsNewerEvents_NoGap()
|
||||
{
|
||||
@@ -194,6 +213,8 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Equal(new ulong[] { 3, 4, 5 }, replay.Select(e => e.WorkerSequence));
|
||||
}
|
||||
|
||||
/// <summary>Retained replay entries older than the retention window are evicted once that window elapses.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task ReplayBuffer_AgedEntries_AreEvictedAfterRetentionElapses()
|
||||
{
|
||||
@@ -228,6 +249,8 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.True(gap);
|
||||
}
|
||||
|
||||
/// <summary>Requesting replay from a sequence newer than everything retained returns an empty list with no gap.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task ReplayBuffer_AfterSequenceNewerThanAllRetained_ReturnsEmpty_NoGap()
|
||||
{
|
||||
@@ -254,6 +277,11 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Empty(replay);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// With replay buffering disabled (capacity 0), a caller behind the highest-seen sequence
|
||||
/// is told there is a gap and gets no replayed events.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task ReplayBuffer_Capacity0_AfterSequenceBelowHighestSeen_ReportsGap_NoEvents()
|
||||
{
|
||||
@@ -281,6 +309,8 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Empty(replay);
|
||||
}
|
||||
|
||||
/// <summary>With replay buffering disabled (capacity 0), a caller already caught up sees no gap and no events.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task ReplayBuffer_Capacity0_AfterSequenceAtOrAboveHighestSeen_NoGap_NoEvents()
|
||||
{
|
||||
@@ -307,6 +337,8 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Empty(replay);
|
||||
}
|
||||
|
||||
/// <summary>When no events have ever been seen, any requested sequence reports no gap and no events.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task ReplayBuffer_NoEventsSeen_AnyAfterSequence_NoGap_NoEvents()
|
||||
{
|
||||
@@ -325,6 +357,11 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Empty(replay);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Requesting replay from <see cref="ulong.MaxValue"/> with retained events present does not
|
||||
/// falsely report a gap from the wrap-around and yields no new events.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task ReplayBuffer_AfterSequenceMaxValue_WithRetainedEvents_NoGap_NoNewEvents()
|
||||
{
|
||||
@@ -349,12 +386,15 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Empty(replay);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Per-subscriber backpressure isolation: one subscriber stops reading and overflows its
|
||||
/// own tiny channel; it is disconnected with an <c>EventQueueOverflow</c> fault while a
|
||||
/// second, healthy subscriber keeps receiving and the pump keeps pumping.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task SlowSubscriberOverflow_DisconnectsOnlyThatSubscriber_PumpAndOtherKeepRunning()
|
||||
{
|
||||
// Per-subscriber backpressure isolation (Task 5): one subscriber stops reading and
|
||||
// overflows its own tiny channel; it is disconnected with an EventQueueOverflow fault
|
||||
// while a second, healthy subscriber keeps receiving and the pump keeps pumping.
|
||||
Channel<MxEvent> source = Channel.CreateUnbounded<MxEvent>();
|
||||
int overflowCalls = 0;
|
||||
// Separate fields for the bool value and the "set" flag so both can use
|
||||
@@ -380,7 +420,7 @@ public sealed class SessionEventDistributorTests
|
||||
singleSubscriberMode: false);
|
||||
await distributor.StartAsync(CancellationToken.None);
|
||||
|
||||
// Slow subscriber: registered but never read, so its capacity-2 channel fills.
|
||||
// Slow subscriber: registered but never read, so its channel (capacity 2) fills.
|
||||
using IEventSubscriberLease slow = distributor.Register();
|
||||
// Healthy subscriber: drains promptly throughout.
|
||||
using IEventSubscriberLease healthy = distributor.Register();
|
||||
@@ -399,7 +439,7 @@ public sealed class SessionEventDistributorTests
|
||||
async () => await DrainUntilFaultAsync(slow.Reader));
|
||||
Assert.Equal(SessionManagerErrorCode.EventQueueOverflow, fault.ErrorCode);
|
||||
|
||||
// Multi-subscriber mode, so isOnlySubscriber is always false (Task 8 mode-gating).
|
||||
// Multi-subscriber mode, so isOnlySubscriber is always false (mode-gating).
|
||||
// Use Interlocked.Read / Volatile.Read so the test-thread reads are ordered after the
|
||||
// pump-thread writes, avoiding a data race by the C# memory model.
|
||||
Assert.Equal(1, Volatile.Read(ref overflowCalls));
|
||||
@@ -413,16 +453,19 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Equal(11ul, afterOverflow.WorkerSequence);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Distributor-level pin for "FailFast with multiple subscribers degrades to
|
||||
/// disconnect-only (no session fault)": in multi-subscriber mode isOnlySubscriber is
|
||||
/// always false (mode-gating), so a FailFast-wired handler must NOT fault the session.
|
||||
/// This test drives the distributor directly (without <c>GatewaySession</c>) in
|
||||
/// multi-subscriber mode with two subscribers and a FailFast-style overflow handler
|
||||
/// seam, overflows the slow one, and asserts (a) isOnlySubscriber==false, (b) the other
|
||||
/// subscriber keeps receiving, and (c) the pump keeps running.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task SlowSubscriberOverflow_WithMultipleSubscribers_HandlerSeesIsOnlySubscriberFalse_OtherKeepsReceiving()
|
||||
{
|
||||
// Distributor-level pin for "FailFast with multiple subscribers degrades to
|
||||
// disconnect-only (no session fault)": in multi-subscriber mode isOnlySubscriber is
|
||||
// always false (Task 8 mode-gating), so a FailFast-wired handler must NOT fault the
|
||||
// session. This test drives the distributor directly (without GatewaySession) in
|
||||
// multi-subscriber mode with two subscribers and a FailFast-style overflow handler
|
||||
// seam, overflows the slow one, and asserts (a) isOnlySubscriber==false, (b) the other
|
||||
// subscriber keeps receiving, and (c) the pump keeps running.
|
||||
Channel<MxEvent> source = Channel.CreateUnbounded<MxEvent>();
|
||||
bool handlerFiredWithFalse = false;
|
||||
bool sessionFaultWouldBeCalled = false; // tracks if a FailFast path would fault
|
||||
@@ -450,7 +493,7 @@ public sealed class SessionEventDistributorTests
|
||||
singleSubscriberMode: false);
|
||||
await distributor.StartAsync(CancellationToken.None);
|
||||
|
||||
// Slow subscriber: never reads, so capacity-2 channel overflows quickly.
|
||||
// Slow subscriber: never reads, so its channel (capacity 2) overflows quickly.
|
||||
using IEventSubscriberLease slow = distributor.Register();
|
||||
// Healthy subscriber: drains every event promptly.
|
||||
using IEventSubscriberLease healthy = distributor.Register();
|
||||
@@ -478,14 +521,17 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Equal(11ul, afterOverflow.WorkerSequence);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that <c>CountExternalSubscribers()</c> excludes the internal dashboard
|
||||
/// subscriber, so a FailFast policy would NOT fault the session even when the internal
|
||||
/// subscriber is the ONLY registered subscriber. The overflow handler receives
|
||||
/// isOnlySubscriber==false (not true) because the overflowing subscriber is internal
|
||||
/// and is therefore excluded from the external-subscriber count.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task InternalSubscriberOverflow_HandlerSeesIsOnlySubscriberFalse_ProvingCountExcludesInternal()
|
||||
{
|
||||
// Issue 3: verifies that CountExternalSubscribers() excludes the internal dashboard
|
||||
// subscriber, so a FailFast policy would NOT fault the session even when the internal
|
||||
// subscriber is the ONLY registered subscriber. The overflow handler receives
|
||||
// isOnlySubscriber==false (not true) because the overflowing subscriber is internal
|
||||
// and is therefore excluded from the external-subscriber count.
|
||||
Channel<MxEvent> source = Channel.CreateUnbounded<MxEvent>();
|
||||
int observedIsOnlySubscriberSet = 0;
|
||||
bool observedIsOnlySubscriberValue = false;
|
||||
@@ -509,7 +555,7 @@ public sealed class SessionEventDistributorTests
|
||||
// Register ONLY an internal subscriber — no external subscriber is attached.
|
||||
using IEventSubscriberLease internalLease = distributor.Register(isInternal: true);
|
||||
|
||||
// Push enough events to overflow the capacity-2 internal subscriber channel.
|
||||
// Push enough events to overflow the internal subscriber channel (capacity 2).
|
||||
for (ulong sequence = 1; sequence <= 10; sequence++)
|
||||
{
|
||||
source.Writer.TryWrite(Event(sequence));
|
||||
@@ -540,13 +586,16 @@ public sealed class SessionEventDistributorTests
|
||||
"isInternal must be true for a subscriber registered with isInternal: true.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mode-gating: in single-subscriber mode a lone external subscriber that overflows
|
||||
/// reports isOnlySubscriber==true, so the legacy FailFast session-fault path is
|
||||
/// preserved. The decision is gated on the fixed session mode, NOT a live count, so it
|
||||
/// is race-free.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task SingleSubscriberMode_LoneExternalOverflow_HandlerSeesIsOnlySubscriberTrue()
|
||||
{
|
||||
// Task 8 mode-gating: in single-subscriber mode a lone external subscriber that
|
||||
// overflows reports isOnlySubscriber==true, so the legacy FailFast session-fault path
|
||||
// is preserved. The decision is gated on the fixed session mode, NOT a live count, so
|
||||
// it is race-free.
|
||||
Channel<MxEvent> source = Channel.CreateUnbounded<MxEvent>();
|
||||
int observedSet = 0;
|
||||
bool observedValue = false;
|
||||
@@ -593,6 +642,11 @@ public sealed class SessionEventDistributorTests
|
||||
"isOnlySubscriber must be true for a lone external subscriber in single-subscriber mode.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registering with replay from a sequence still inside the retained window returns the
|
||||
/// newer retained events with no gap, then continues to deliver live events.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task RegisterWithReplay_WithinRetainedWindow_ReturnsNewerEvents_NoGap_ThenLive()
|
||||
{
|
||||
@@ -631,6 +685,8 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Equal(6ul, live.WorkerSequence);
|
||||
}
|
||||
|
||||
/// <summary>Registering with replay from a sequence below the oldest retained event reports a gap along with the oldest available sequence.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task RegisterWithReplay_BelowOldestRetained_ReportsGap_AndOldestAvailable()
|
||||
{
|
||||
@@ -662,6 +718,8 @@ public sealed class SessionEventDistributorTests
|
||||
Assert.Equal(5ul, liveResume);
|
||||
}
|
||||
|
||||
/// <summary>When nothing retained is newer than the requested sequence, the live resume watermark equals the requested sequence and no gap is reported.</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task RegisterWithReplay_NothingRetainedNewer_LiveResumeEqualsAfterSequence_NoGap()
|
||||
{
|
||||
@@ -733,6 +791,7 @@ public sealed class SessionEventDistributorTests
|
||||
/// <c>GatewaySessionDashboardMirrorTests</c>, where a gRPC subscriber attached after a
|
||||
/// fast-completing worker stream had already drained.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task Register_AfterSourceCompletes_CompletesLateSubscriberInsteadOfHanging()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user