test(gateway): deterministic multi-subscriber test sync + cap-rejection specificity
Replace Task.Delay(100) subscriber-attachment races with WaitForSubscriberCountAsync, a polling gate on GatewaySession.ActiveEventSubscriberCount so Advise and event fan-out cannot proceed until all subscribers are confirmed registered. Fix WaitForMessageCountAsync to honor a single CancellationTokenSource deadline across the poll loop rather than resetting the timeout on each intermediate wakeup. Add ordering comment in the cancellation test explaining why stream1Task must be awaited before AllowNextEvent to guarantee sub1 is unregistered before the 2nd event is fanned. Assert capException.Status.Detail contains "maximum" in the cap test to distinguish EventSubscriberLimitReached (AllowMultiple=true cap) from EventSubscriberAlreadyActive (single-subscriber rejection) — both map to ResourceExhausted. Extract shared ConfigureCommandReply helper and move FakeWorkerProcess to TestSupport/ so both fake-worker test classes reference one definition.
This commit is contained in:
@@ -533,63 +533,4 @@ public sealed class GatewayEndToEndFakeWorkerSmokeTests
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class FakeWorkerProcess(int processId) : IWorkerProcess
|
||||
{
|
||||
private readonly TaskCompletionSource _exited = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the process identifier.
|
||||
/// </summary>
|
||||
public int Id { get; } = processId;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the process has exited.
|
||||
/// </summary>
|
||||
public bool HasExited { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the exit code of the process.
|
||||
/// </summary>
|
||||
public int? ExitCode { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Waits for the process to exit asynchronously. Completes only when <see cref="Kill"/>
|
||||
/// or <see cref="MarkExited"/> has been called, so callers that observe completion can
|
||||
/// trust that exit actually happened (e.g., via the worker shutdown-ack path).
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>A task that completes when the process has actually exited.</returns>
|
||||
public ValueTask WaitForExitAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return new ValueTask(_exited.Task.WaitAsync(cancellationToken));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Terminates the process.
|
||||
/// </summary>
|
||||
/// <param name="entireProcessTree">Whether to kill the entire process tree.</param>
|
||||
public void Kill(bool entireProcessTree)
|
||||
{
|
||||
MarkExited(-1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases resources used by this process.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the process as exited with the specified exit code.
|
||||
/// </summary>
|
||||
/// <param name="exitCode">The process exit code.</param>
|
||||
public void MarkExited(int exitCode)
|
||||
{
|
||||
HasExited = true;
|
||||
ExitCode = exitCode;
|
||||
_exited.TrySetResult();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user