Skip to content

Commit b4fa5d9

Browse files
Resolve .NET E2E race conditions in cancellation and multi-client tests (#1034)
1 parent 6565a3b commit b4fa5d9

File tree

2 files changed

+19
-0
lines changed

2 files changed

+19
-0
lines changed

dotnet/test/MultiClientTests.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ public async Task One_Client_Approves_Permission_And_Both_See_The_Result()
170170
var client1Events = new ConcurrentBag<SessionEvent>();
171171
var client2Events = new ConcurrentBag<SessionEvent>();
172172

173+
// Wait for PermissionCompletedEvent on client2 which may arrive slightly after session1 goes idle
174+
var client2PermissionCompleted = TestHelper.GetNextEventOfTypeAsync<PermissionCompletedEvent>(session2);
175+
173176
using var sub1 = session1.On(evt => client1Events.Add(evt));
174177
using var sub2 = session2.On(evt => client2Events.Add(evt));
175178

@@ -181,6 +184,8 @@ public async Task One_Client_Approves_Permission_And_Both_See_The_Result()
181184
Assert.NotNull(response);
182185
Assert.NotEmpty(client1PermissionRequests);
183186

187+
await client2PermissionCompleted;
188+
184189
Assert.Contains(client1Events, e => e is PermissionRequestedEvent);
185190
Assert.Contains(client2Events, e => e is PermissionRequestedEvent);
186191
Assert.Contains(client1Events, e => e is PermissionCompletedEvent);

dotnet/test/SessionTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,11 +432,18 @@ public async Task SendAndWait_Throws_On_Timeout()
432432
{
433433
var session = await CreateSessionAsync();
434434

435+
var sessionIdleTask = TestHelper.GetNextEventOfTypeAsync<SessionIdleEvent>(session);
436+
435437
// Use a slow command to ensure timeout triggers before completion
436438
var ex = await Assert.ThrowsAsync<TimeoutException>(() =>
437439
session.SendAndWaitAsync(new MessageOptions { Prompt = "Run 'sleep 2 && echo done'" }, TimeSpan.FromMilliseconds(100)));
438440

439441
Assert.Contains("timed out", ex.Message);
442+
443+
// The timeout only cancels the client-side wait; abort the agent and wait for idle
444+
// so leftover requests don't leak into subsequent tests.
445+
await session.AbortAsync();
446+
await sessionIdleTask;
440447
}
441448

442449
[Fact]
@@ -446,6 +453,7 @@ public async Task SendAndWait_Throws_OperationCanceledException_When_Token_Cance
446453

447454
// Set up wait for tool execution to start BEFORE sending
448455
var toolStartTask = TestHelper.GetNextEventOfTypeAsync<ToolExecutionStartEvent>(session);
456+
var sessionIdleTask = TestHelper.GetNextEventOfTypeAsync<SessionIdleEvent>(session);
449457

450458
using var cts = new CancellationTokenSource();
451459

@@ -461,6 +469,12 @@ public async Task SendAndWait_Throws_OperationCanceledException_When_Token_Cance
461469
cts.Cancel();
462470

463471
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => sendTask);
472+
473+
// Cancelling the token only cancels the client-side wait, not the server-side agent loop.
474+
// Explicitly abort so the agent stops, then wait for idle to ensure we're not still
475+
// running this agent's operations in the context of a subsequent test.
476+
await session.AbortAsync();
477+
await sessionIdleTask;
464478
}
465479

466480
[Fact]

0 commit comments

Comments
 (0)