Skip to content

Commit eadb432

Browse files
author
Esteban Solano
committed
+ code coverage for ClientOptions/Capabilities code
1 parent 2e1b931 commit eadb432

File tree

2 files changed

+103
-31
lines changed

2 files changed

+103
-31
lines changed

tests/ModelContextProtocol.Tests/Client/McpClientExtensionsTests.cs

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public async Task CreateSamplingHandler_ShouldHandleTextMessages(float? temperat
7979
var handler = McpClientExtensions.CreateSamplingHandler(mockChatClient.Object);
8080

8181
// Act
82-
var result = await handler(requestParams, cancellationToken);
82+
var result = await handler(requestParams, Mock.Of<IProgress<ProgressNotificationValue>>(), cancellationToken);
8383

8484
// Assert
8585
Assert.NotNull(result);
@@ -96,19 +96,19 @@ public async Task CreateSamplingHandler_ShouldHandleImageMessages()
9696
var mockChatClient = new Mock<IChatClient>();
9797
var requestParams = new CreateMessageRequestParams
9898
{
99-
Messages = new[]
100-
{
101-
new SamplingMessage
102-
{
103-
Role = Role.User,
104-
Content = new Content
99+
Messages =
100+
[
101+
new SamplingMessage
105102
{
106-
Type = "image",
107-
MimeType = "image/png",
108-
Data = Convert.ToBase64String(new byte[] { 1, 2, 3 })
103+
Role = Role.User,
104+
Content = new Content
105+
{
106+
Type = "image",
107+
MimeType = "image/png",
108+
Data = Convert.ToBase64String(new byte[] { 1, 2, 3 })
109+
}
109110
}
110-
}
111-
},
111+
],
112112
MaxTokens = 100
113113
};
114114
var cancellationToken = CancellationToken.None;
@@ -127,7 +127,7 @@ public async Task CreateSamplingHandler_ShouldHandleImageMessages()
127127
var handler = McpClientExtensions.CreateSamplingHandler(mockChatClient.Object);
128128

129129
// Act
130-
var result = await handler(requestParams, cancellationToken);
130+
var result = await handler(requestParams, Mock.Of<IProgress<ProgressNotificationValue>>(), cancellationToken);
131131

132132
// Assert
133133
Assert.NotNull(result);
@@ -144,24 +144,23 @@ public async Task CreateSamplingHandler_ShouldHandleResourceMessages()
144144
var mockChatClient = new Mock<IChatClient>();
145145
var requestParams = new CreateMessageRequestParams
146146
{
147-
Messages = new[]
148-
{
149-
new SamplingMessage
150-
{
151-
Role = Role.User,
152-
Content = new Content
147+
Messages =
148+
[
149+
new SamplingMessage
153150
{
154-
Type = "resource",
155-
Resource = new ResourceContents
151+
Role = Role.User,
152+
Content = new Content
156153
{
157-
Text = "Resource text",
158-
Blob = Convert.ToBase64String(new byte[] { 4, 5, 6 }),
159-
MimeType = "application/octet-stream"
154+
Type = "resource",
155+
Resource = new BlobResourceContents
156+
{
157+
Blob = "Resource text",
158+
MimeType = "application/octet-stream"
159+
}
160160
}
161161
}
162-
}
163-
},
164-
MaxTokens = 100
162+
],
163+
MaxTokens = 100
165164
};
166165
var cancellationToken = CancellationToken.None;
167166

@@ -179,7 +178,7 @@ public async Task CreateSamplingHandler_ShouldHandleResourceMessages()
179178
var handler = McpClientExtensions.CreateSamplingHandler(mockChatClient.Object);
180179

181180
// Act
182-
var result = await handler(requestParams, cancellationToken);
181+
var result = await handler(requestParams, Mock.Of<IProgress<ProgressNotificationValue>>(), cancellationToken);
183182

184183
// Assert
185184
Assert.NotNull(result);
@@ -189,7 +188,7 @@ public async Task CreateSamplingHandler_ShouldHandleResourceMessages()
189188
Assert.Equal("endTurn", result.StopReason);
190189
}
191190

192-
public ValueTask DisposeAsync()
191+
public async ValueTask DisposeAsync()
193192
{
194193
await _cts.CancelAsync();
195194

tests/ModelContextProtocol.Tests/Client/McpClientFactoryTests.cs

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
using Microsoft.Extensions.Logging;
12
using ModelContextProtocol.Client;
23
using ModelContextProtocol.Protocol.Messages;
34
using ModelContextProtocol.Protocol.Transport;
45
using ModelContextProtocol.Protocol.Types;
6+
using Moq;
57
using System.Text.Json;
68
using System.Threading.Channels;
79

@@ -187,7 +189,68 @@ public async Task McpFactory_WithInvalidTransportOptions_ThrowsFormatException(s
187189
await Assert.ThrowsAsync<ArgumentException>(() => McpClientFactory.CreateAsync(config, _defaultOptions, cancellationToken: TestContext.Current.CancellationToken));
188190
}
189191

190-
private sealed class NopTransport : ITransport, IClientTransport
192+
[Theory]
193+
[InlineData(typeof(NopTransport))]
194+
[InlineData(typeof(FailureTransport))]
195+
public async Task CreateAsync_WithCapabilitiesOptions(Type transportType)
196+
{
197+
// Arrange
198+
var serverConfig = new McpServerConfig
199+
{
200+
Id = "TestServer",
201+
Name = "TestServer",
202+
TransportType = "stdio",
203+
Location = "test-location"
204+
};
205+
206+
var clientOptions = new McpClientOptions
207+
{
208+
ClientInfo = new Implementation
209+
{
210+
Name = "TestClient",
211+
Version = "1.0.0.0"
212+
},
213+
Capabilities = new ClientCapabilities
214+
{
215+
Sampling = new SamplingCapability
216+
{
217+
SamplingHandler = (c, p, t) => Task.FromResult(
218+
new CreateMessageResult {
219+
Content = new Content { Text = "result" },
220+
Model = "test-model",
221+
Role = "test-role",
222+
StopReason = "endTurn"
223+
}),
224+
},
225+
Roots = new RootsCapability
226+
{
227+
ListChanged = true,
228+
RootsHandler = (t, r) => Task.FromResult(new ListRootsResult { Roots = [] }),
229+
}
230+
}
231+
};
232+
233+
var clientTransport = (IClientTransport?)Activator.CreateInstance(transportType);
234+
IMcpClient? client = null;
235+
236+
var actionTask = McpClientFactory.CreateAsync(serverConfig, clientOptions, (config, logger) => clientTransport ?? new NopTransport(), new Mock<ILoggerFactory>().Object, CancellationToken.None);
237+
238+
// Act
239+
if (clientTransport is FailureTransport)
240+
{
241+
var exception = await Assert.ThrowsAsync<InvalidOperationException>(async() => await actionTask);
242+
Assert.Equal(FailureTransport.ExpectedMessage, exception.Message);
243+
}
244+
else
245+
{
246+
client = await actionTask;
247+
248+
// Assert
249+
Assert.NotNull(client);
250+
}
251+
}
252+
253+
private class NopTransport : ITransport, IClientTransport
191254
{
192255
private readonly Channel<IJsonRpcMessage> _channel = Channel.CreateUnbounded<IJsonRpcMessage>();
193256

@@ -199,7 +262,7 @@ private sealed class NopTransport : ITransport, IClientTransport
199262

200263
public ValueTask DisposeAsync() => default;
201264

202-
public Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default)
265+
public virtual Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default)
203266
{
204267
switch (message)
205268
{
@@ -224,4 +287,14 @@ public Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancella
224287
return Task.CompletedTask;
225288
}
226289
}
290+
291+
private sealed class FailureTransport : NopTransport
292+
{
293+
public const string ExpectedMessage = "Something failed";
294+
295+
public override Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default)
296+
{
297+
throw new InvalidOperationException(ExpectedMessage);
298+
}
299+
}
227300
}

0 commit comments

Comments
 (0)