Upgrade to xunit.v3 (#8)

* Upgrade to xunit.v3

* Use Assert.Skip where applicable.

---------

Co-authored-by: Eirik Tsarpalis <eirik.tsarpalis@gmail.com>
This commit is contained in:
Stephen Toub 2025-03-20 10:19:35 -04:00 • committed by GitHub
commit 27ba8b1b6a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 128 additions and 110 deletions

View file

@ -35,7 +35,7 @@
<PackageVersion Include="Serilog.Sinks.Debug" Version="3.0.0" /> <PackageVersion Include="Serilog.Sinks.Debug" Version="3.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" /> <PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
<PackageVersion Include="System.Linq.AsyncEnumerable" Version="$(System10Version)" /> <PackageVersion Include="System.Linq.AsyncEnumerable" Version="$(System10Version)" />
<PackageVersion Include="xunit" Version="2.9.2" /> <PackageVersion Include="xunit.v3" Version="1.1.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.0" /> <PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -31,9 +31,11 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B6FB2B28-D5DE-4654-BE9A-45E305DE4852}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B6FB2B28-D5DE-4654-BE9A-45E305DE4852}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props Directory.Build.props = Directory.Build.props
Directory.Packages.props = Directory.Packages.props
global.json = global.json global.json = global.json
LICENSE = LICENSE LICENSE = LICENSE
logo.png = logo.png logo.png = logo.png
nuget.config = nuget.config
README.MD = README.MD README.MD = README.MD
version.json = version.json version.json = version.json
EndProjectSection EndProjectSection

View file

@ -1,5 +1,4 @@
using System.Text; using System.Text;
using System.Text.Json;
using ModelContextProtocol.Protocol.Messages; using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Protocol.Transport; using ModelContextProtocol.Protocol.Transport;
using ModelContextProtocol.Protocol.Types; using ModelContextProtocol.Protocol.Types;

View file

@ -17,34 +17,28 @@ public class McpClientFactoryTests
[Fact] [Fact]
public async Task CreateAsync_WithInvalidArgs_Throws() public async Task CreateAsync_WithInvalidArgs_Throws()
{ {
await Assert.ThrowsAsync<ArgumentNullException>("serverConfig", () => McpClientFactory.CreateAsync((McpServerConfig)null!, _defaultOptions)); await Assert.ThrowsAsync<ArgumentNullException>("serverConfig", () => McpClientFactory.CreateAsync((McpServerConfig)null!, _defaultOptions, cancellationToken: TestContext.Current.CancellationToken));
await Assert.ThrowsAsync<ArgumentNullException>("clientOptions", () => McpClientFactory.CreateAsync( await Assert.ThrowsAsync<ArgumentNullException>("clientOptions", () => McpClientFactory.CreateAsync(new McpServerConfig()
new McpServerConfig()
{ {
Name = "name", Name = "name",
Id = "id", Id = "id",
TransportType = TransportTypes.StdIo, TransportType = TransportTypes.StdIo,
}, (McpClientOptions)null!)); }, (McpClientOptions)null!, cancellationToken: TestContext.Current.CancellationToken));
await Assert.ThrowsAsync<ArgumentException>("serverConfig", () => McpClientFactory.CreateAsync( await Assert.ThrowsAsync<ArgumentException>("serverConfig", () => McpClientFactory.CreateAsync(new McpServerConfig()
new McpServerConfig()
{ {
Name = "name", Name = "name",
Id = "id", Id = "id",
TransportType = "somethingunsupported", TransportType = "somethingunsupported",
}, }, _defaultOptions, cancellationToken: TestContext.Current.CancellationToken));
_defaultOptions));
await Assert.ThrowsAsync<InvalidOperationException>(() => McpClientFactory.CreateAsync( await Assert.ThrowsAsync<InvalidOperationException>(() => McpClientFactory.CreateAsync(new McpServerConfig()
new McpServerConfig()
{ {
Name = "name", Name = "name",
Id = "id", Id = "id",
TransportType = TransportTypes.StdIo, TransportType = TransportTypes.StdIo,
}, }, _defaultOptions, (_, __) => null!, cancellationToken: TestContext.Current.CancellationToken));
_defaultOptions,
(_, __) => null!));
} }
[Fact] [Fact]
@ -68,7 +62,8 @@ public class McpClientFactoryTests
await using var client = await McpClientFactory.CreateAsync( await using var client = await McpClientFactory.CreateAsync(
serverConfig, serverConfig,
_defaultOptions, _defaultOptions,
(_, __) => new NopTransport()); (_, __) => new NopTransport(),
cancellationToken: TestContext.Current.CancellationToken);
// Assert // Assert
Assert.NotNull(client); Assert.NotNull(client);
@ -91,7 +86,8 @@ public class McpClientFactoryTests
await using var client = await McpClientFactory.CreateAsync( await using var client = await McpClientFactory.CreateAsync(
serverConfig, serverConfig,
_defaultOptions, _defaultOptions,
(_, __) => new NopTransport()); (_, __) => new NopTransport(),
cancellationToken: TestContext.Current.CancellationToken);
// Assert // Assert
Assert.NotNull(client); Assert.NotNull(client);
@ -114,7 +110,8 @@ public class McpClientFactoryTests
await using var client = await McpClientFactory.CreateAsync( await using var client = await McpClientFactory.CreateAsync(
serverConfig, serverConfig,
_defaultOptions, _defaultOptions,
(_, __) => new NopTransport()); (_, __) => new NopTransport(),
cancellationToken: TestContext.Current.CancellationToken);
// Assert // Assert
Assert.NotNull(client); Assert.NotNull(client);
@ -144,7 +141,8 @@ public class McpClientFactoryTests
await using var client = await McpClientFactory.CreateAsync( await using var client = await McpClientFactory.CreateAsync(
serverConfig, serverConfig,
_defaultOptions, _defaultOptions,
(_, __) => new NopTransport()); (_, __) => new NopTransport(),
cancellationToken: TestContext.Current.CancellationToken);
// Assert // Assert
Assert.NotNull(client); Assert.NotNull(client);
@ -171,7 +169,7 @@ public class McpClientFactoryTests
}; };
// act & assert // act & assert
await Assert.ThrowsAsync<ArgumentException>(() => McpClientFactory.CreateAsync(config, _defaultOptions)); await Assert.ThrowsAsync<ArgumentException>(() => McpClientFactory.CreateAsync(config, _defaultOptions, cancellationToken: TestContext.Current.CancellationToken));
} }
private sealed class NopTransport : IClientTransport private sealed class NopTransport : IClientTransport
@ -191,7 +189,7 @@ public class McpClientFactoryTests
{ {
switch (message) switch (message)
{ {
case JsonRpcRequest request: case JsonRpcRequest:
_channel.Writer.TryWrite(new JsonRpcResponse _channel.Writer.TryWrite(new JsonRpcResponse
{ {
Id = ((JsonRpcRequest)message).Id, Id = ((JsonRpcRequest)message).Id,

View file

@ -1,11 +1,12 @@
using ModelContextProtocol.Client; using ModelContextProtocol.Client;
using ModelContextProtocol.Configuration;
using ModelContextProtocol.Protocol.Messages;
using ModelContextProtocol.Protocol.Transport;
using ModelContextProtocol.Protocol.Types;
using Microsoft.Extensions.AI; using Microsoft.Extensions.AI;
using OpenAI; using OpenAI;
using ModelContextProtocol.Protocol.Types;
using ModelContextProtocol.Protocol.Messages;
using System.Text.Json; using System.Text.Json;
using ModelContextProtocol.Configuration;
using ModelContextProtocol.Protocol.Transport;
using Xunit.Sdk;
namespace ModelContextProtocol.Tests; namespace ModelContextProtocol.Tests;
@ -61,8 +62,8 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
// act // act
await using var client = await _fixture.CreateClientAsync(clientId); await using var client = await _fixture.CreateClientAsync(clientId);
var tools = await client.ListToolsAsync().ToListAsync(); var tools = await client.ListToolsAsync(TestContext.Current.CancellationToken).ToListAsync(TestContext.Current.CancellationToken);
var aiFunctions = await client.GetAIFunctionsAsync(); var aiFunctions = await client.GetAIFunctionsAsync(TestContext.Current.CancellationToken);
// assert // assert
Assert.NotEmpty(tools); Assert.NotEmpty(tools);
@ -102,9 +103,9 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
// act // act
await using var client = await _fixture.CreateClientAsync(clientId); await using var client = await _fixture.CreateClientAsync(clientId);
var aiFunctions = await client.GetAIFunctionsAsync(); var aiFunctions = await client.GetAIFunctionsAsync(TestContext.Current.CancellationToken);
var echo = aiFunctions.Single(t => t.Name == "echo"); var echo = aiFunctions.Single(t => t.Name == "echo");
var result = await echo.InvokeAsync([new KeyValuePair<string, object?>("message", "Hello MCP!")]); var result = await echo.InvokeAsync([new KeyValuePair<string, object?>("message", "Hello MCP!")], TestContext.Current.CancellationToken);
// assert // assert
Assert.NotNull(result); Assert.NotNull(result);
@ -119,7 +120,7 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
// act // act
await using var client = await _fixture.CreateClientAsync(clientId); await using var client = await _fixture.CreateClientAsync(clientId);
var prompts = await client.ListPromptsAsync().ToListAsync(); var prompts = await client.ListPromptsAsync(TestContext.Current.CancellationToken).ToListAsync(TestContext.Current.CancellationToken);
// assert // assert
Assert.NotEmpty(prompts); Assert.NotEmpty(prompts);
@ -251,7 +252,7 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
await client.SubscribeToResourceAsync("test://static/resource/1", CancellationToken.None); await client.SubscribeToResourceAsync("test://static/resource/1", CancellationToken.None);
// notifications happen every 5 seconds, so we wait for 10 seconds to ensure we get at least one notification // notifications happen every 5 seconds, so we wait for 10 seconds to ensure we get at least one notification
await Task.Delay(10000); await Task.Delay(10000, TestContext.Current.CancellationToken);
// assert // assert
Assert.True(counter > 0); Assert.True(counter > 0);
@ -276,17 +277,17 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
await client.SubscribeToResourceAsync("test://static/resource/1", CancellationToken.None); await client.SubscribeToResourceAsync("test://static/resource/1", CancellationToken.None);
// notifications happen every 5 seconds, so we wait for 10 seconds to ensure we get at least one notification // notifications happen every 5 seconds, so we wait for 10 seconds to ensure we get at least one notification
await Task.Delay(10000); await Task.Delay(10000, TestContext.Current.CancellationToken);
// reset counter // reset counter
int counterAfterSubscribe = counter; int counterAfterSubscribe = counter;
// unsubscribe // unsubscribe
await client.UnsubscribeFromResourceAsync("test://static/resource/1", CancellationToken.None); await client.UnsubscribeFromResourceAsync("test://static/resource/1", CancellationToken.None);
counter = 0; counter = 0;
// notifications happen every 5 seconds, so we wait for 10 seconds to ensure we would've gotten at least one notification // notifications happen every 5 seconds, so we wait for 10 seconds to ensure we would've gotten at least one notification
await Task.Delay(10000); await Task.Delay(10000, TestContext.Current.CancellationToken);
// assert // assert
Assert.True(counterAfterSubscribe > 0); Assert.True(counterAfterSubscribe > 0);
@ -340,7 +341,7 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
[Theory] [Theory]
[MemberData(nameof(GetClients))] [MemberData(nameof(GetClients))]
public async Task Sampling_Stdio(string clientId) public async Task Sampling_Stdio(string clientId)
{ {
// Set up the sampling handler // Set up the sampling handler
int samplingHandlerCalls = 0; int samplingHandlerCalls = 0;
await using var client = await _fixture.CreateClientAsync(clientId, new() await using var client = await _fixture.CreateClientAsync(clientId, new()
@ -375,8 +376,8 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
{ {
["prompt"] = "Test prompt", ["prompt"] = "Test prompt",
["maxTokens"] = 100 ["maxTokens"] = 100
} },
); TestContext.Current.CancellationToken);
// assert // assert
Assert.NotNull(result); Assert.NotNull(result);
@ -423,8 +424,8 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
await using var client = await _fixture.CreateClientAsync(clientId); await using var client = await _fixture.CreateClientAsync(clientId);
// Verify we can send notifications without errors // Verify we can send notifications without errors
await client.SendNotificationAsync(NotificationMethods.RootsUpdatedNotification); await client.SendNotificationAsync(NotificationMethods.RootsUpdatedNotification, cancellationToken: TestContext.Current.CancellationToken);
await client.SendNotificationAsync("test/notification", new { test = true }); await client.SendNotificationAsync("test/notification", new { test = true }, TestContext.Current.CancellationToken);
// assert // assert
// no response to check, if no exception is thrown, it's a success // no response to check, if no exception is thrown, it's a success
@ -452,13 +453,17 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
ClientInfo = new() { Name = "IntegrationTestClient", Version = "1.0.0" } ClientInfo = new() { Name = "IntegrationTestClient", Version = "1.0.0" }
}; };
await using var client = await McpClientFactory.CreateAsync(serverConfig, clientOptions, loggerFactory: _fixture.LoggerFactory); await using var client = await McpClientFactory.CreateAsync(
serverConfig,
clientOptions,
loggerFactory: _fixture.LoggerFactory,
cancellationToken: TestContext.Current.CancellationToken);
// act // act
var result = await client.CallToolAsync( var result = await client.CallToolAsync(
"read_graph", "read_graph",
[] [],
); TestContext.Current.CancellationToken);
// assert // assert
Assert.NotNull(result); Assert.NotNull(result);
@ -471,14 +476,14 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
[Fact] [Fact]
public async Task GetAIFunctionsAsync_UsingEverythingServer_ToolsAreProperlyCalled() public async Task GetAIFunctionsAsync_UsingEverythingServer_ToolsAreProperlyCalled()
{ {
if (s_openAIKey is null) SkipTestIfNoOpenAIKey();
{
return; // Skip the test if the OpenAI key is not provided
}
// Get the MCP client and tools from it. // Get the MCP client and tools from it.
await using var client = await McpClientFactory.CreateAsync(_fixture.EverythingServerConfig, _fixture.DefaultOptions); ; await using var client = await McpClientFactory.CreateAsync(
var mappedTools = await client.GetAIFunctionsAsync(); _fixture.EverythingServerConfig,
_fixture.DefaultOptions,
cancellationToken: TestContext.Current.CancellationToken);
var mappedTools = await client.GetAIFunctionsAsync(TestContext.Current.CancellationToken);
// Create the chat client. // Create the chat client.
using IChatClient chatClient = new OpenAIClient(s_openAIKey).AsChatClient("gpt-4o-mini") using IChatClient chatClient = new OpenAIClient(s_openAIKey).AsChatClient("gpt-4o-mini")
@ -495,7 +500,7 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
messages.Add(new(ChatRole.User, "Please call the echo tool with the string 'Hello MCP!' and output the response ad verbatim.")); messages.Add(new(ChatRole.User, "Please call the echo tool with the string 'Hello MCP!' and output the response ad verbatim."));
// Call the chat client // Call the chat client
var response = await chatClient.GetResponseAsync(messages, new() { Tools = [.. mappedTools], Temperature = 0 }); var response = await chatClient.GetResponseAsync(messages, new() { Tools = [.. mappedTools], Temperature = 0 }, TestContext.Current.CancellationToken);
// Assert // Assert
Assert.Contains("Echo: Hello MCP!", response.Text); Assert.Contains("Echo: Hello MCP!", response.Text);
@ -504,10 +509,7 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
[Fact] [Fact]
public async Task SamplingViaChatClient_RequestResponseProperlyPropagated() public async Task SamplingViaChatClient_RequestResponseProperlyPropagated()
{ {
if (s_openAIKey is null) SkipTestIfNoOpenAIKey();
{
return; // Skip the test if the OpenAI key is not provided
}
await using var client = await McpClientFactory.CreateAsync(_fixture.EverythingServerConfig, new() await using var client = await McpClientFactory.CreateAsync(_fixture.EverythingServerConfig, new()
{ {
@ -519,12 +521,12 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
SamplingHandler = new OpenAIClient(s_openAIKey).AsChatClient("gpt-4o-mini").CreateSamplingHandler(), SamplingHandler = new OpenAIClient(s_openAIKey).AsChatClient("gpt-4o-mini").CreateSamplingHandler(),
}, },
}, },
}); }, cancellationToken: TestContext.Current.CancellationToken);
var result = await client.CallToolAsync("sampleLLM", new() var result = await client.CallToolAsync("sampleLLM", new()
{ {
["prompt"] = "In just a few words, what is the most famous tower in Paris?", ["prompt"] = "In just a few words, what is the most famous tower in Paris?",
}); }, TestContext.Current.CancellationToken);
Assert.NotNull(result); Assert.NotNull(result);
Assert.NotEmpty(result.Content); Assert.NotEmpty(result.Content);
@ -532,4 +534,9 @@ public class ClientIntegrationTests : IClassFixture<ClientIntegrationTestFixture
Assert.Contains("LLM sampling result:", result.Content[0].Text); Assert.Contains("LLM sampling result:", result.Content[0].Text);
Assert.Contains("Eiffel", result.Content[0].Text); Assert.Contains("Eiffel", result.Content[0].Text);
} }
private static void SkipTestIfNoOpenAIKey()
{
Assert.SkipWhen(s_openAIKey is null, "No OpenAI key provided. Skipping test.");
}
} }

View file

@ -18,7 +18,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" /> <PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" /> <PackageReference Include="Moq" />
<PackageReference Include="System.Linq.AsyncEnumerable" /> <PackageReference Include="System.Linq.AsyncEnumerable" />
<PackageReference Include="xunit" /> <PackageReference Include="xunit.v3" />
<PackageReference Include="xunit.runner.visualstudio"> <PackageReference Include="xunit.runner.visualstudio">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>

View file

@ -7,7 +7,6 @@ using ModelContextProtocol.Tests.Utils;
using Microsoft.Extensions.AI; using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Moq; using Moq;
using System.Diagnostics;
namespace ModelContextProtocol.Tests.Server; namespace ModelContextProtocol.Tests.Server;
@ -100,7 +99,7 @@ public class McpServerTests
server.GetType().GetField("_isInitializing", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.SetValue(server, true); server.GetType().GetField("_isInitializing", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)?.SetValue(server, true);
// Act & Assert // Act & Assert
await Assert.ThrowsAsync<InvalidOperationException>(() => server.StartAsync()); await Assert.ThrowsAsync<InvalidOperationException>(() => server.StartAsync(TestContext.Current.CancellationToken));
} }
[Fact] [Fact]
@ -110,7 +109,7 @@ public class McpServerTests
await using var server = new McpServer(_serverTransport.Object, _options, _loggerFactory.Object, _serviceProvider); await using var server = new McpServer(_serverTransport.Object, _options, _loggerFactory.Object, _serviceProvider);
server.IsInitialized = true; server.IsInitialized = true;
await server.StartAsync(); await server.StartAsync(TestContext.Current.CancellationToken);
// Assert // Assert
_serverTransport.Verify(t => t.StartListeningAsync(It.IsAny<CancellationToken>()), Times.Never); _serverTransport.Verify(t => t.StartListeningAsync(It.IsAny<CancellationToken>()), Times.Never);
@ -123,7 +122,7 @@ public class McpServerTests
await using var server = new McpServer(_serverTransport.Object, _options, _loggerFactory.Object, _serviceProvider); await using var server = new McpServer(_serverTransport.Object, _options, _loggerFactory.Object, _serviceProvider);
// Act // Act
await server.StartAsync(); await server.StartAsync(TestContext.Current.CancellationToken);
// Assert // Assert
_serverTransport.Verify(t => t.StartListeningAsync(It.IsAny<CancellationToken>()), Times.Once); _serverTransport.Verify(t => t.StartListeningAsync(It.IsAny<CancellationToken>()), Times.Once);
@ -135,17 +134,16 @@ public class McpServerTests
await using var transport = new TestServerTransport(); await using var transport = new TestServerTransport();
await using var server = new McpServer(transport, _options, _loggerFactory.Object, _serviceProvider); await using var server = new McpServer(transport, _options, _loggerFactory.Object, _serviceProvider);
await server.StartAsync(); await server.StartAsync(TestContext.Current.CancellationToken);
// Send initialized notification // Send initialized notification
await transport.SendMessageAsync( await transport.SendMessageAsync(new JsonRpcNotification
new JsonRpcNotification
{ {
Method = "notifications/initialized" Method = "notifications/initialized"
} }
); , TestContext.Current.CancellationToken);
await Task.Delay(50); await Task.Delay(50, TestContext.Current.CancellationToken);
Assert.True(server.IsInitialized); Assert.True(server.IsInitialized);
} }
@ -171,7 +169,7 @@ public class McpServerTests
await using var server = new McpServer(transport, _options, _loggerFactory.Object, _serviceProvider); await using var server = new McpServer(transport, _options, _loggerFactory.Object, _serviceProvider);
server.ClientCapabilities = new ClientCapabilities { Sampling = new SamplingCapability() }; server.ClientCapabilities = new ClientCapabilities { Sampling = new SamplingCapability() };
await server.StartAsync(); await server.StartAsync(TestContext.Current.CancellationToken);
// Act // Act
var result = await server.RequestSamplingAsync(new CreateMessageRequestParams { Messages = [] }, CancellationToken.None); var result = await server.RequestSamplingAsync(new CreateMessageRequestParams { Messages = [] }, CancellationToken.None);
@ -200,7 +198,7 @@ public class McpServerTests
await using var transport = new TestServerTransport(); await using var transport = new TestServerTransport();
await using var server = new McpServer(transport, _options, _loggerFactory.Object, _serviceProvider); await using var server = new McpServer(transport, _options, _loggerFactory.Object, _serviceProvider);
server.ClientCapabilities = new ClientCapabilities { Roots = new RootsCapability() }; server.ClientCapabilities = new ClientCapabilities { Roots = new RootsCapability() };
await server.StartAsync(); await server.StartAsync(TestContext.Current.CancellationToken);
// Act // Act
var result = await server.RequestRootsAsync(new ListRootsRequestParams(), CancellationToken.None); var result = await server.RequestRootsAsync(new ListRootsRequestParams(), CancellationToken.None);
@ -588,7 +586,7 @@ public class McpServerTests
Temperature = 0.75f, Temperature = 0.75f,
MaxOutputTokens = 42, MaxOutputTokens = 42,
StopSequences = ["."], StopSequences = ["."],
}); }, TestContext.Current.CancellationToken);
Assert.Equal("amazingmodel", response.ModelId); Assert.Equal("amazingmodel", response.ModelId);
Assert.Equal(ChatFinishReason.Stop, response.FinishReason); Assert.Equal(ChatFinishReason.Stop, response.FinishReason);

View file

@ -37,13 +37,17 @@ public class SseIntegrationTests
}; };
// Act // Act
await using var client = await McpClientFactory.CreateAsync(defaultConfig, defaultOptions, loggerFactory: loggerFactory); await using var client = await McpClientFactory.CreateAsync(
defaultConfig,
defaultOptions,
loggerFactory: loggerFactory,
cancellationToken: TestContext.Current.CancellationToken);
// Wait for SSE connection to be established // Wait for SSE connection to be established
await server.WaitForConnectionAsync(TimeSpan.FromSeconds(10)); await server.WaitForConnectionAsync(TimeSpan.FromSeconds(10));
// Send a test message through POST endpoint // Send a test message through POST endpoint
await client.SendNotificationAsync("test/message", new { message = "Hello, SSE!" }); await client.SendNotificationAsync("test/message", new { message = "Hello, SSE!" }, TestContext.Current.CancellationToken);
// Assert // Assert
Assert.True(true); Assert.True(true);
@ -53,10 +57,7 @@ public class SseIntegrationTests
[Trait("Execution", "Manual")] [Trait("Execution", "Manual")]
public async Task ConnectAndReceiveMessage_EverythingServerWithSse() public async Task ConnectAndReceiveMessage_EverythingServerWithSse()
{ {
if (!EverythingSseServerFixture.IsDockerAvailable) Assert.SkipWhen(!EverythingSseServerFixture.IsDockerAvailable, "docker is not available");
{
return;
}
using var loggerFactory = LoggerFactory.Create(builder => using var loggerFactory = LoggerFactory.Create(builder =>
builder.AddConsole() builder.AddConsole()
@ -82,8 +83,12 @@ public class SseIntegrationTests
}; };
// Create client and run tests // Create client and run tests
await using var client = await McpClientFactory.CreateAsync(defaultConfig, defaultOptions, loggerFactory: loggerFactory); await using var client = await McpClientFactory.CreateAsync(
var tools = await client.ListToolsAsync().ToListAsync(); defaultConfig,
defaultOptions,
loggerFactory: loggerFactory,
cancellationToken: TestContext.Current.CancellationToken);
var tools = await client.ListToolsAsync(TestContext.Current.CancellationToken).ToListAsync(TestContext.Current.CancellationToken);
// assert // assert
Assert.NotEmpty(tools); Assert.NotEmpty(tools);
@ -93,10 +98,7 @@ public class SseIntegrationTests
[Trait("Execution", "Manual")] [Trait("Execution", "Manual")]
public async Task Sampling_Sse_EverythingServer() public async Task Sampling_Sse_EverythingServer()
{ {
if (!EverythingSseServerFixture.IsDockerAvailable) Assert.SkipWhen(!EverythingSseServerFixture.IsDockerAvailable, "docker is not available");
{
return;
}
// arrange // arrange
using var loggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder => using var loggerFactory = Microsoft.Extensions.Logging.LoggerFactory.Create(builder =>
@ -147,17 +149,19 @@ public class SseIntegrationTests
}, },
}; };
await using var client = await McpClientFactory.CreateAsync(defaultConfig, defaultOptions, loggerFactory: loggerFactory); await using var client = await McpClientFactory.CreateAsync(
defaultConfig,
defaultOptions,
loggerFactory: loggerFactory,
cancellationToken: TestContext.Current.CancellationToken);
// Call the server's sampleLLM tool which should trigger our sampling handler // Call the server's sampleLLM tool which should trigger our sampling handler
var result = await client.CallToolAsync( var result = await client.CallToolAsync("sampleLLM", new Dictionary<string, object>
"sampleLLM",
new Dictionary<string, object>
{ {
["prompt"] = "Test prompt", ["prompt"] = "Test prompt",
["maxTokens"] = 100 ["maxTokens"] = 100
} }
); , TestContext.Current.CancellationToken);
// assert // assert
Assert.NotNull(result); Assert.NotNull(result);
@ -194,13 +198,17 @@ public class SseIntegrationTests
}; };
// Act // Act
await using var client = await McpClientFactory.CreateAsync(defaultConfig, defaultOptions, loggerFactory: loggerFactory); await using var client = await McpClientFactory.CreateAsync(
defaultConfig,
defaultOptions,
loggerFactory: loggerFactory,
cancellationToken: TestContext.Current.CancellationToken);
// Wait for SSE connection to be established // Wait for SSE connection to be established
await server.WaitForConnectionAsync(TimeSpan.FromSeconds(10)); await server.WaitForConnectionAsync(TimeSpan.FromSeconds(10));
// Send a test message through POST endpoint // Send a test message through POST endpoint
await client.SendNotificationAsync("test/message", new { message = "Hello, SSE!" }); await client.SendNotificationAsync("test/message", new { message = "Hello, SSE!" }, TestContext.Current.CancellationToken);
// Assert // Assert
Assert.True(true); Assert.True(true);
@ -233,7 +241,11 @@ public class SseIntegrationTests
}; };
// Act // Act
await using var client = await McpClientFactory.CreateAsync(defaultConfig, defaultOptions, loggerFactory: loggerFactory); await using var client = await McpClientFactory.CreateAsync(
defaultConfig,
defaultOptions,
loggerFactory: loggerFactory,
cancellationToken: TestContext.Current.CancellationToken);
// Wait for SSE connection to be established // Wait for SSE connection to be established
await server.WaitForConnectionAsync(TimeSpan.FromSeconds(10)); await server.WaitForConnectionAsync(TimeSpan.FromSeconds(10));
@ -251,7 +263,7 @@ public class SseIntegrationTests
await server.SendTestNotificationAsync("Hello from server!"); await server.SendTestNotificationAsync("Hello from server!");
// Assert // Assert
var message = await receivedNotification.Task.WaitAsync(TimeSpan.FromSeconds(10)); var message = await receivedNotification.Task.WaitAsync(TimeSpan.FromSeconds(10), TestContext.Current.CancellationToken);
Assert.Equal("Hello from server!", message); Assert.Equal("Hello from server!", message);
} }
@ -282,7 +294,11 @@ public class SseIntegrationTests
}; };
// Act // Act
await using var client = await McpClientFactory.CreateAsync(defaultConfig, defaultOptions, loggerFactory: loggerFactory); await using var client = await McpClientFactory.CreateAsync(
defaultConfig,
defaultOptions,
loggerFactory: loggerFactory,
cancellationToken: TestContext.Current.CancellationToken);
var mcpClient = (McpClient)client; var mcpClient = (McpClient)client;
var transport = (SseClientTransport)mcpClient.Transport; var transport = (SseClientTransport)mcpClient.Transport;
@ -290,6 +306,6 @@ public class SseIntegrationTests
await server.WaitForConnectionAsync(TimeSpan.FromSeconds(10)); await server.WaitForConnectionAsync(TimeSpan.FromSeconds(10));
// Assert // Assert
await Assert.ThrowsAsync<McpTransportException>(async () => await transport.ConnectAsync()); await Assert.ThrowsAsync<McpTransportException>(async () => await transport.ConnectAsync(TestContext.Current.CancellationToken));
} }
} }

View file

@ -53,7 +53,7 @@ public class SseServerIntegrationTests : IClassFixture<SseServerIntegrationTestF
// act // act
var client = await GetClientAsync(); var client = await GetClientAsync();
var tools = await client.ListToolsAsync().ToListAsync(); var tools = await client.ListToolsAsync(TestContext.Current.CancellationToken).ToListAsync(TestContext.Current.CancellationToken);
// assert // assert
Assert.NotNull(tools); Assert.NotNull(tools);
@ -145,7 +145,7 @@ public class SseServerIntegrationTests : IClassFixture<SseServerIntegrationTestF
// act // act
var client = await GetClientAsync(); var client = await GetClientAsync();
var prompts = await client.ListPromptsAsync().ToListAsync(); var prompts = await client.ListPromptsAsync(TestContext.Current.CancellationToken).ToListAsync(TestContext.Current.CancellationToken);
// assert // assert
Assert.NotNull(prompts); Assert.NotNull(prompts);
@ -233,14 +233,12 @@ public class SseServerIntegrationTests : IClassFixture<SseServerIntegrationTestF
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
// Call the server's sampleLLM tool which should trigger our sampling handler // Call the server's sampleLLM tool which should trigger our sampling handler
var result = await client.CallToolAsync( var result = await client.CallToolAsync("sampleLLM", new Dictionary<string, object>
"sampleLLM",
new Dictionary<string, object>
{ {
["prompt"] = "Test prompt", ["prompt"] = "Test prompt",
["maxTokens"] = 100 ["maxTokens"] = 100
} },
); TestContext.Current.CancellationToken);
// assert // assert
Assert.NotNull(result); Assert.NotNull(result);

View file

@ -95,7 +95,7 @@ public class SseClientTransportTests
}; };
}; };
await transport.ConnectAsync(); await transport.ConnectAsync(TestContext.Current.CancellationToken);
} }
[Fact] [Fact]
@ -174,7 +174,7 @@ public class SseClientTransportTests
} }
}; };
await transport.ConnectAsync(); await transport.ConnectAsync(TestContext.Current.CancellationToken);
await transport.SendMessageAsync(new JsonRpcRequest() { Method = "initialize", Id = RequestId.FromNumber(44) }, CancellationToken.None); await transport.SendMessageAsync(new JsonRpcRequest() { Method = "initialize", Id = RequestId.FromNumber(44) }, CancellationToken.None);
Assert.True(true); Assert.True(true);
@ -213,7 +213,7 @@ public class SseClientTransportTests
} }
}; };
await transport.ConnectAsync(); await transport.ConnectAsync(TestContext.Current.CancellationToken);
await transport.SendMessageAsync(new JsonRpcRequest() { Method = "initialize", Id = RequestId.FromNumber(44) }, CancellationToken.None); await transport.SendMessageAsync(new JsonRpcRequest() { Method = "initialize", Id = RequestId.FromNumber(44) }, CancellationToken.None);
Assert.True(true); Assert.True(true);
@ -251,7 +251,7 @@ public class SseClientTransportTests
throw new IOException("Abort"); throw new IOException("Abort");
}; };
await transport.ConnectAsync(); await transport.ConnectAsync(TestContext.Current.CancellationToken);
Assert.True(transport.MessageReader.TryRead(out var message)); Assert.True(transport.MessageReader.TryRead(out var message));
Assert.NotNull(message); Assert.NotNull(message);
Assert.IsType<JsonRpcRequest>(message); Assert.IsType<JsonRpcRequest>(message);

View file

@ -52,7 +52,7 @@ public class StdioServerTransportTests
{ {
await using var transport = new StdioServerTransport(_serverOptions); await using var transport = new StdioServerTransport(_serverOptions);
await transport.StartListeningAsync(); await transport.StartListeningAsync(TestContext.Current.CancellationToken);
Assert.True(transport.IsConnected); Assert.True(transport.IsConnected);
} }
@ -70,12 +70,12 @@ public class StdioServerTransportTests
Console.SetOut(output); Console.SetOut(output);
await using var transport = new StdioServerTransport(_serverOptions, NullLoggerFactory.Instance); await using var transport = new StdioServerTransport(_serverOptions, NullLoggerFactory.Instance);
await transport.StartListeningAsync(); await transport.StartListeningAsync(TestContext.Current.CancellationToken);
var message = new JsonRpcRequest { Method = "test", Id = RequestId.FromNumber(44) }; var message = new JsonRpcRequest { Method = "test", Id = RequestId.FromNumber(44) };
await transport.SendMessageAsync(message); await transport.SendMessageAsync(message, TestContext.Current.CancellationToken);
var result = output.ToString()?.Trim(); var result = output.ToString()?.Trim();
var expected = JsonSerializer.Serialize(message, McpJsonUtilities.DefaultOptions); var expected = JsonSerializer.Serialize(message, McpJsonUtilities.DefaultOptions);
@ -96,7 +96,7 @@ public class StdioServerTransportTests
var message = new JsonRpcRequest { Method = "test" }; var message = new JsonRpcRequest { Method = "test" };
await Assert.ThrowsAsync<McpTransportException>(() => transport.SendMessageAsync(message)); await Assert.ThrowsAsync<McpTransportException>(() => transport.SendMessageAsync(message, TestContext.Current.CancellationToken));
} }
[Fact] [Fact]
@ -123,9 +123,9 @@ public class StdioServerTransportTests
Console.SetOut(new StringWriter()); Console.SetOut(new StringWriter());
await using var transport = new StdioServerTransport(_serverOptions); await using var transport = new StdioServerTransport(_serverOptions);
await transport.StartListeningAsync(); await transport.StartListeningAsync(TestContext.Current.CancellationToken);
var canRead = await transport.MessageReader.WaitToReadAsync(); var canRead = await transport.MessageReader.WaitToReadAsync(TestContext.Current.CancellationToken);
Assert.True(canRead, "Nothing to read here from transport message reader"); Assert.True(canRead, "Nothing to read here from transport message reader");
Assert.True(transport.MessageReader.TryPeek(out var readMessage)); Assert.True(transport.MessageReader.TryPeek(out var readMessage));
@ -144,7 +144,7 @@ public class StdioServerTransportTests
public async Task CleanupAsync_Should_Cleanup_Resources() public async Task CleanupAsync_Should_Cleanup_Resources()
{ {
var transport = new StdioServerTransport(_serverOptions); var transport = new StdioServerTransport(_serverOptions);
await transport.StartListeningAsync(); await transport.StartListeningAsync(TestContext.Current.CancellationToken);
await transport.DisposeAsync(); await transport.DisposeAsync();