Skip to content

Commit d195bd6

Browse files
committed
Add cancellation tests
1 parent 8701faa commit d195bd6

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using ModelContextProtocol.Client;
3+
using ModelContextProtocol.Protocol.Messages;
4+
using ModelContextProtocol.Server;
5+
6+
namespace ModelContextProtocol.Tests;
7+
8+
public class CancellationTests : ClientServerTestBase
9+
{
10+
public CancellationTests(ITestOutputHelper testOutputHelper)
11+
: base(testOutputHelper)
12+
{
13+
}
14+
15+
protected override void ConfigureServices(ServiceCollection services, IMcpServerBuilder mcpServerBuilder)
16+
{
17+
services.AddSingleton(McpServerTool.Create(WaitForCancellation));
18+
}
19+
20+
private static async Task WaitForCancellation(CancellationToken cancellationToken)
21+
{
22+
try
23+
{
24+
await Task.Delay(-1, cancellationToken);
25+
throw new InvalidOperationException("Unexpected completion without exception");
26+
}
27+
catch (OperationCanceledException)
28+
{
29+
return;
30+
}
31+
}
32+
33+
[Fact]
34+
public async Task PrecancelRequest_CancelsBeforeSending()
35+
{
36+
var client = await CreateMcpClientForServer();
37+
38+
bool gotCancellation = false;
39+
await using (Server.RegisterNotificationHandler(NotificationMethods.CancelledNotification, (notification, cancellationToken) =>
40+
{
41+
gotCancellation = true;
42+
return Task.CompletedTask;
43+
}))
44+
{
45+
await Assert.ThrowsAsync<OperationCanceledException>(() => client.ListToolsAsync(cancellationToken: new CancellationToken(true)));
46+
}
47+
48+
Assert.False(gotCancellation);
49+
}
50+
51+
[Fact]
52+
public async Task CancellationPropagation_RequestingCancellationCancelsPendingRequest()
53+
{
54+
var client = await CreateMcpClientForServer();
55+
56+
var tools = await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken);
57+
var waitTool = tools.First(t => t.Name == nameof(WaitForCancellation));
58+
59+
CancellationTokenSource cts = new();
60+
var waitTask = waitTool.InvokeAsync(cancellationToken: cts.Token);
61+
Assert.False(waitTask.IsCompleted);
62+
63+
await Task.Delay(1, TestContext.Current.CancellationToken);
64+
Assert.False(waitTask.IsCompleted);
65+
66+
cts.Cancel();
67+
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await waitTask);
68+
}
69+
}

0 commit comments

Comments
 (0)