test(integration): ShutdownWorker live fact pins reply-then-exit; log gateway session state
This commit is contained in:
@@ -1160,8 +1160,8 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output)
|
||||
/// <summary>
|
||||
/// TST-05: <c>ShutdownWorker</c> writes its OK reply before the worker shuts down, so the
|
||||
/// reply reaches the caller (a reply written after the pipe closed would surface as a
|
||||
/// pipe-disconnected failure instead), and the worker process then exits on its own and the
|
||||
/// session leaves <see cref="SessionState.Ready"/>. Uses its own fixture and worker because it
|
||||
/// pipe-disconnected failure instead), the worker process then exits on its own, and a later
|
||||
/// command on the session does not succeed. Uses its own fixture and worker because it
|
||||
/// terminates the worker.
|
||||
/// </summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
@@ -1221,29 +1221,40 @@ public sealed class WorkerLiveMxAccessSmokeTests(ITestOutputHelper output)
|
||||
// throws on timeout, which fails the test.
|
||||
await processFactory.WaitForProcessesAsync(StreamShutdownTimeout).ConfigureAwait(false);
|
||||
|
||||
DateTimeOffset waitDeadline = DateTimeOffset.UtcNow + StreamShutdownTimeout;
|
||||
SessionState observedState = SessionState.Ready;
|
||||
while (DateTimeOffset.UtcNow < waitDeadline)
|
||||
// The session can no longer serve commands once its worker is gone: either the
|
||||
// gateway rejects the call outright or the reply is not Ok.
|
||||
MxCommandReply? afterReply = null;
|
||||
Exception? afterException = null;
|
||||
try
|
||||
{
|
||||
if (!fixture.TryGetSession(sessionId, out GatewaySession? session))
|
||||
{
|
||||
observedState = SessionState.Closed;
|
||||
break;
|
||||
}
|
||||
|
||||
observedState = session.State;
|
||||
if (observedState is SessionState.Faulted or SessionState.Closed or SessionState.Closing)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(50)).ConfigureAwait(false);
|
||||
afterReply = await fixture.Service.Invoke(
|
||||
CreateControlRequest(sessionId, "live-ping-after-shutdown", new MxCommand
|
||||
{
|
||||
Kind = MxCommandKind.Ping,
|
||||
Ping = new PingCommand(),
|
||||
}),
|
||||
new TestServerCallContext()).ConfigureAwait(false);
|
||||
LogReply("PingAfterShutdown", afterReply);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
afterException = exception;
|
||||
output.WriteLine($"PingAfterShutdown rejected: {exception.GetType().Name}: {exception.Message}");
|
||||
}
|
||||
|
||||
output.WriteLine($"ShutdownWorker observed_state={observedState}");
|
||||
Assert.True(
|
||||
observedState is SessionState.Faulted or SessionState.Closed or SessionState.Closing,
|
||||
$"Expected the session to leave Ready after ShutdownWorker; observed {observedState}.");
|
||||
afterException is not null || afterReply!.ProtocolStatus.Code != ProtocolStatusCode.Ok,
|
||||
"A command sent after ShutdownWorker must not succeed against the exited worker.");
|
||||
|
||||
// Logged, not asserted: the gateway's WorkerClient moves to Closed on the shutdown
|
||||
// ack, but GatewaySession.State is not driven from that transition today, so the
|
||||
// public session state still reads Ready here (observed live on windev). That is a
|
||||
// gateway-side gap, not a worker one — this fact pins the worker half of the
|
||||
// contract: OK reply first, then a clean exit.
|
||||
string observedState = fixture.TryGetSession(sessionId, out GatewaySession? session)
|
||||
? session.State.ToString()
|
||||
: "<removed>";
|
||||
output.WriteLine($"ShutdownWorker observed_session_state={observedState}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user