Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions tests/Common/Utils/TestConstants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace ModelContextProtocol.Tests.Utils;

/// <summary>
/// Provides centralized constants for tests
/// </summary>
public static class TestConstants
Comment thread
ericstj marked this conversation as resolved.
{
/// <summary>
/// Default timeout for test operations that may be affected by CI machine load.
/// Set to 60 seconds to provide sufficient buffer for slow CI environments.
/// </summary>
public static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(60);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using ModelContextProtocol.Tests.Utils;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -251,7 +252,7 @@ public async Task CanResumeSessionWithMapMcpAndRunSessionHandler()
Assert.NotNull(serverInfo);
Assert.False(string.IsNullOrEmpty(resumedSessionId));

await serverTcs.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
await serverTcs.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);

await using var resumeTransport = new HttpClientTransport(new()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using ModelContextProtocol.Tests.Utils;

namespace ModelContextProtocol.AspNetCore.Tests;

Expand Down Expand Up @@ -279,7 +280,7 @@ public async Task Client_CanResumePostResponseStream_AfterDisconnection()
[Fact]
public async Task Client_CanResumeUnsolicitedMessageStream_AfterDisconnection()
{
var timeout = TimeSpan.FromSeconds(10);
var timeout = TestConstants.DefaultTimeout;
using var faultingStreamHandler = new FaultingStreamHandler()
{
InnerHandler = SocketsHttpHandler,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public async ValueTask DisposeAsync()
{
try
{
await _serverTask.WaitAsync(TimeSpan.FromSeconds(5));
await _serverTask.WaitAsync(TestConstants.DefaultTimeout);
}
catch
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using ModelContextProtocol.Tests.Utils;
using System.Text.Json.Serialization;
using TestServerWithHosting.Tools;

Expand Down Expand Up @@ -110,7 +111,7 @@ public async Task ConnectAndReceiveNotification_InMemoryServer()
// Send a test message through POST endpoint
await mcpClient.SendNotificationAsync("test/notification", new Envelope { Message = "Hello from client!" }, serializerOptions: JsonContext.Default.Options, cancellationToken: TestContext.Current.CancellationToken);

var message = await receivedNotification.Task.WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken);
var message = await receivedNotification.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);
Assert.Equal("Hello from server!", message);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using ModelContextProtocol.Tests.Utils;
using System.Threading;
using System.Threading.Tasks;
using System.Text.Json;
Expand Down Expand Up @@ -245,7 +246,7 @@ public async Task ResumeSessionStartsGetImmediately()
loggerFactory: LoggerFactory,
cancellationToken: TestContext.Current.CancellationToken))
{
var observedSessionId = await resumeServer.GetStarted.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
var observedSessionId = await resumeServer.GetStarted.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);
Assert.Equal(sessionId, observedSessionId);

var tools = await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ public async Task MultipleConcurrentJsonRpcRequests_IsHandled_InParallel()
await Task.WhenAll(echoTasks);
}

[Fact]
[Fact(Skip = "https://github.com/modelcontextprotocol/csharp-sdk/issues/1211")]
public async Task GetRequest_Receives_UnsolicitedNotifications()
{
McpServer? server = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using ModelContextProtocol.Tests.Utils;
using System.ComponentModel;

namespace ModelContextProtocol.Tests.Client;
Expand Down Expand Up @@ -50,7 +51,7 @@ await Server.SendNotificationAsync(
cancellationToken: TestContext.Current.CancellationToken);

// Assert
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
using var cts = new CancellationTokenSource(TestConstants.DefaultTimeout);
var receivedNotification = await notificationReceived.Task.WaitAsync(cts.Token);
Assert.NotNull(receivedNotification);
Assert.Equal(resourceUri, receivedNotification.Uri);
Expand Down Expand Up @@ -92,7 +93,7 @@ await Server.SendNotificationAsync(
cancellationToken: TestContext.Current.CancellationToken);

// Assert
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
using var cts = new CancellationTokenSource(TestConstants.DefaultTimeout);
await correctNotificationReceived.Task.WaitAsync(cts.Token);

// Give a small delay to ensure no other notifications are processed
Expand Down Expand Up @@ -168,7 +169,7 @@ await Server.SendNotificationAsync(
cancellationToken: TestContext.Current.CancellationToken);

// Assert
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
using var cts = new CancellationTokenSource(TestConstants.DefaultTimeout);
var receivedNotification = await notificationReceived.Task.WaitAsync(cts.Token);
Assert.NotNull(receivedNotification);
Assert.Equal(resourceUri.AbsoluteUri, receivedNotification.Uri);
Expand Down Expand Up @@ -263,7 +264,7 @@ await Server.SendNotificationAsync(
cancellationToken: TestContext.Current.CancellationToken);

// Assert
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
using var cts = new CancellationTokenSource(TestConstants.DefaultTimeout);
var combined = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, TestContext.Current.CancellationToken);
await Task.WhenAll(
notification1Received.Task.WaitAsync(combined.Token),
Expand Down Expand Up @@ -341,7 +342,7 @@ await Server.SendNotificationAsync(
cancellationToken: TestContext.Current.CancellationToken);

// Assert - Both handlers should be invoked
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
using var cts = new CancellationTokenSource(TestConstants.DefaultTimeout);
var combined = CancellationTokenSource.CreateLinkedTokenSource(cts.Token, TestContext.Current.CancellationToken);
await Task.WhenAll(
handler1Called.Task.WaitAsync(combined.Token),
Expand All @@ -360,7 +361,7 @@ await Server.SendNotificationAsync(
cancellationToken: TestContext.Current.CancellationToken);

// Wait for handler2 to be called again
using var cts2 = new CancellationTokenSource(TimeSpan.FromSeconds(5));
using var cts2 = new CancellationTokenSource(TestConstants.DefaultTimeout);
var combined2 = CancellationTokenSource.CreateLinkedTokenSource(cts2.Token, TestContext.Current.CancellationToken);
await handler2CalledAgain.Task.WaitAsync(combined2.Token);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using ModelContextProtocol.Tests.Utils;
using System.Text.Json;

namespace ModelContextProtocol.Tests.Client;
Expand Down Expand Up @@ -219,7 +220,7 @@ public async Task Client_WithTaskStore_CanExecuteSamplingAsTask()
Assert.Equal(McpTaskStatus.Working, mcpTask.Status);

// Wait for sampling to complete
await samplingCompleted.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
await samplingCompleted.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);

// Poll until task is complete
McpTask taskStatus;
Expand Down Expand Up @@ -375,7 +376,7 @@ public async Task Client_WithTaskStore_CanExecuteElicitationAsTask()
Assert.Equal(McpTaskStatus.Working, mcpTask.Status);

// Wait for elicitation to complete
await elicitationCompleted.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
await elicitationCompleted.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);

// Poll until task is complete
McpTask taskStatus;
Expand Down Expand Up @@ -488,7 +489,7 @@ public async Task Client_CanCancelTasks()
TestContext.Current.CancellationToken);

// Wait for sampling to start
await samplingStarted.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
await samplingStarted.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);

// Act - Cancel the task
var cancelledTask = await Server.CancelTaskAsync(mcpTask.TaskId, TestContext.Current.CancellationToken);
Expand Down Expand Up @@ -600,8 +601,8 @@ public async Task Client_TaskStatusNotifications_SentWhenEnabled()
// Wait for both Working and Completed notifications to arrive
// The notifications are sent asynchronously so we need to wait for both
await Task.WhenAll(
workingNotificationReceived.Task.WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken),
completedNotificationReceived.Task.WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken));
workingNotificationReceived.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken),
completedNotificationReceived.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken));

// Assert - Should have received notifications for status transitions
await notificationHandler.DisposeAsync();
Expand Down Expand Up @@ -653,7 +654,7 @@ public async Task Client_SamplingHandlerException_ResultsInFailedTask()
TestContext.Current.CancellationToken);

// Wait for sampling attempt
await samplingAttempted.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
await samplingAttempted.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);

// Poll until task status changes
McpTask taskStatus;
Expand Down Expand Up @@ -703,7 +704,7 @@ public async Task Client_ElicitationHandlerException_ResultsInFailedTask()
TestContext.Current.CancellationToken);

// Wait for elicitation attempt
await elicitationAttempted.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
await elicitationAttempted.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);

// Poll until task status changes
McpTask taskStatus;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Tests.Utils;

namespace ModelContextProtocol.Tests.Configuration;

Expand Down Expand Up @@ -275,7 +276,7 @@ public async Task Can_Elicit_OutOfBand_With_Url()
Assert.NotNull(capturedMessage);
Assert.Contains(capturedElicitationId, capturedUrl);

var notifiedElicitationId = await completionNotification.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
var notifiedElicitationId = await completionNotification.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);
Assert.Equal(capturedElicitationId, notifiedElicitationId);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public async Task TaskStatus_TransitionsToInputRequired_DuringSampleAsync()
cancellationToken: TestContext.Current.CancellationToken);

// Wait for the sampling request to be received by the client
await samplingRequestReceived.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
await samplingRequestReceived.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);

// Check the task status while sampling is in progress
var statusDuringSampling = await taskStore.GetTaskAsync(
Expand Down Expand Up @@ -194,7 +194,7 @@ public async Task TaskStatus_TransitionsToInputRequired_DuringElicitAsync()
cancellationToken: TestContext.Current.CancellationToken);

// Wait for the elicitation request to be received
await elicitationRequestReceived.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
await elicitationRequestReceived.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);

// Check the task status while elicitation is in progress
var statusDuringElicitation = await taskStore.GetTaskAsync(
Expand Down Expand Up @@ -301,7 +301,7 @@ public async Task TaskStatus_ReturnsToWorking_AfterSamplingCompletes()
cancellationToken: TestContext.Current.CancellationToken);

// Wait for sampling to complete inside the tool
await samplingCompleted.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
await samplingCompleted.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);

// Small delay to ensure status update is processed
await Task.Delay(50, TestContext.Current.CancellationToken);
Expand Down Expand Up @@ -380,7 +380,7 @@ public async Task TaskStatus_DoesNotChangeToInputRequired_ForNonTaskExecution()
arguments: new Dictionary<string, object?> { ["prompt"] = "Hello" },
cancellationToken: TestContext.Current.CancellationToken);

await samplingCompleted.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
await samplingCompleted.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);

// Assert - No task should exist (tool was not called as a task)
var tasks = await taskStore.ListTasksAsync(cancellationToken: TestContext.Current.CancellationToken);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -755,7 +755,7 @@ private static async Task InitializeServerAsync(TestServerTransport transport, C
await transport.SendClientMessageAsync(initializeRequest, cancellationToken);

// Wait for the initialize response to be sent
await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5), cancellationToken);
await tcs.Task.WaitAsync(TestConstants.DefaultTimeout, cancellationToken);
}

#endregion
Expand Down
8 changes: 4 additions & 4 deletions tests/ModelContextProtocol.Tests/Server/McpServerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -692,7 +692,7 @@ await transport.SendMessageAsync(
TestContext.Current.CancellationToken
);

var error = await receivedMessage.Task.WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken);
var error = await receivedMessage.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);
Assert.NotNull(error);
Assert.NotNull(error.Error);
Assert.Equal((int)errorCode, error.Error.Code);
Expand Down Expand Up @@ -744,7 +744,7 @@ await transport.SendMessageAsync(
TestContext.Current.CancellationToken
);

var error = await receivedMessage.Task.WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken);
var error = await receivedMessage.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);
Assert.NotNull(error);
Assert.NotNull(error.Error);
Assert.Equal((int)ErrorCode, error.Error.Code);
Expand Down Expand Up @@ -786,7 +786,7 @@ await transport.SendMessageAsync(
}
);

var response = await receivedMessage.Task.WaitAsync(TimeSpan.FromSeconds(10));
var response = await receivedMessage.Task.WaitAsync(TestConstants.DefaultTimeout);
Assert.NotNull(response);

assertResult(server, response.Result);
Expand Down Expand Up @@ -887,7 +887,7 @@ private static async Task InitializeServerAsync(TestServerTransport transport, C
await transport.SendClientMessageAsync(initializeRequest, cancellationToken);

// Wait for the initialize response to be sent
await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5), cancellationToken);
await tcs.Task.WaitAsync(TestConstants.DefaultTimeout, cancellationToken);
}

private sealed class TestServerForIChatClient(bool supportsSampling) : McpServer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using ModelContextProtocol.Server;
using ModelContextProtocol.Tests.Utils;
using System.Text.Json;

namespace ModelContextProtocol.Tests.Server;
Expand Down Expand Up @@ -77,10 +78,10 @@ public async Task TaskTool_CancellationToken_FiresWhenTtlExpires()
Assert.NotNull(callResult.Task);

// Wait for the tool to start executing
await _toolStarted.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
await _toolStarted.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);

// Assert - Wait for the cancellation to fire (should happen when TTL expires)
var cancelled = await _toolCancellationFired.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
var cancelled = await _toolCancellationFired.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);
Assert.True(cancelled, "Tool's CancellationToken should have been triggered when TTL expired");

// Note: TTL-based expiration does not explicitly set task status to Cancelled.
Expand Down Expand Up @@ -108,13 +109,13 @@ public async Task TaskTool_CancellationToken_FiresWhenExplicitlyCancelled()
string taskId = callResult.Task.TaskId;

// Wait for the tool to start executing
await _toolStarted.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
await _toolStarted.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);

// Act - Explicitly cancel the task
var cancelledTask = await client.CancelTaskAsync(taskId, cancellationToken: TestContext.Current.CancellationToken);

// Assert - Wait for the cancellation to propagate to the tool
var cancelled = await _toolCancellationFired.Task.WaitAsync(TimeSpan.FromSeconds(5), TestContext.Current.CancellationToken);
var cancelled = await _toolCancellationFired.Task.WaitAsync(TestConstants.DefaultTimeout, TestContext.Current.CancellationToken);
Assert.True(cancelled, "Tool's CancellationToken should have been triggered by explicit cancellation");

// Verify task status
Expand Down Expand Up @@ -255,15 +256,15 @@ private Task WaitForStart(string marker, CancellationToken ct)
{
lock (_lock)
{
return _toolStarts[marker].Task.WaitAsync(TimeSpan.FromSeconds(5), ct);
return _toolStarts[marker].Task.WaitAsync(TestConstants.DefaultTimeout, ct);
}
}

private Task<bool> WaitForCancellation(string marker, CancellationToken ct)
{
lock (_lock)
{
return _toolCancellations[marker].Task.WaitAsync(TimeSpan.FromSeconds(5), ct);
return _toolCancellations[marker].Task.WaitAsync(TestConstants.DefaultTimeout, ct);
}
}

Expand Down