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:
@@ -0,0 +1,47 @@
|
||||
using ZB.MOM.WW.MxGateway.Server.Workers;
|
||||
|
||||
namespace ZB.MOM.WW.MxGateway.Tests.TestSupport;
|
||||
|
||||
/// <summary>
|
||||
/// Lightweight in-process stand-in for <see cref="IWorkerProcess"/> used by fake worker
|
||||
/// launchers in end-to-end tests. Call <see cref="MarkExited"/> from the fake worker
|
||||
/// body once the shutdown-ack handshake is complete so that callers awaiting
|
||||
/// <see cref="WaitForExitAsync"/> observe real exit rather than a timeout.
|
||||
/// </summary>
|
||||
public 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, or <see langword="null"/> if it has not exited.</summary>
|
||||
public int? ExitCode { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public ValueTask WaitForExitAsync(CancellationToken cancellationToken) =>
|
||||
new(_exited.Task.WaitAsync(cancellationToken));
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Kill(bool entireProcessTree) => MarkExited(-1);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Marks the process as exited with the specified exit code and unblocks
|
||||
/// any callers of <see cref="WaitForExitAsync"/>.
|
||||
/// </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