Skip to content

Commit 861f6fe

Browse files
Copilotstephentoub
andcommitted
Simplify delegation tests: remove Moq, use sentinel inner types, consolidate into single test per type, fix net472 build
Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
1 parent 5755349 commit 861f6fe

3 files changed

Lines changed: 64 additions & 201 deletions

File tree

Lines changed: 20 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,31 @@
11
using ModelContextProtocol.Protocol;
22
using ModelContextProtocol.Server;
3-
using Moq;
43
using System.Reflection;
54

65
namespace ModelContextProtocol.Tests.Server;
76

87
public class DelegatingMcpServerPromptTests
98
{
10-
private static JsonRpcRequest CreateTestJsonRpcRequest() => new()
11-
{
12-
Id = new RequestId("test-id"),
13-
Method = "test/method",
14-
Params = null,
15-
};
16-
179
[Fact]
1810
public void Ctor_NullInnerPrompt_Throws()
1911
{
20-
Assert.Throws<ArgumentNullException>("innerPrompt", () => new TestDelegatingMcpServerPrompt(null!));
21-
}
22-
23-
[Fact]
24-
public void ProtocolPrompt_DelegatesToInnerPrompt()
25-
{
26-
Prompt expected = new() { Name = "test-prompt" };
27-
Mock<McpServerPrompt> mock = new();
28-
mock.Setup(p => p.ProtocolPrompt).Returns(expected);
29-
30-
TestDelegatingMcpServerPrompt delegating = new(mock.Object);
31-
32-
Assert.Same(expected, delegating.ProtocolPrompt);
33-
mock.Verify(p => p.ProtocolPrompt, Times.Once);
34-
}
35-
36-
[Fact]
37-
public void Metadata_DelegatesToInnerPrompt()
38-
{
39-
IReadOnlyList<object> expected = new object[] { "attr1", "attr2" };
40-
Mock<McpServerPrompt> mock = new();
41-
mock.Setup(p => p.Metadata).Returns(expected);
42-
43-
TestDelegatingMcpServerPrompt delegating = new(mock.Object);
44-
45-
Assert.Same(expected, delegating.Metadata);
46-
mock.Verify(p => p.Metadata, Times.Once);
47-
}
48-
49-
[Fact]
50-
public async Task GetAsync_DelegatesToInnerPrompt()
51-
{
52-
GetPromptResult expected = new() { Messages = [] };
53-
RequestContext<GetPromptRequestParams> request = new(new Mock<McpServer>().Object, CreateTestJsonRpcRequest());
54-
using CancellationTokenSource cts = new();
55-
Mock<McpServerPrompt> mock = new();
56-
mock.Setup(p => p.GetAsync(request, cts.Token)).ReturnsAsync(expected);
57-
58-
TestDelegatingMcpServerPrompt delegating = new(mock.Object);
59-
60-
GetPromptResult result = await delegating.GetAsync(request, cts.Token);
61-
62-
Assert.Same(expected, result);
63-
mock.Verify(p => p.GetAsync(request, cts.Token), Times.Once);
12+
Assert.Throws<ArgumentNullException>("innerPrompt", () => new TestDelegatingPrompt(null!));
6413
}
6514

6615
[Fact]
67-
public void ToString_DelegatesToInnerPrompt()
16+
public async Task AllMembers_DelegateToInnerPrompt()
6817
{
69-
Mock<McpServerPrompt> mock = new();
70-
mock.Setup(p => p.ToString()).Returns("inner-prompt-string");
18+
Prompt expectedPrompt = new() { Name = "sentinel-prompt" };
19+
IReadOnlyList<object> expectedMetadata = new object[] { "m1" };
20+
GetPromptResult expectedResult = new() { Messages = [] };
21+
InnerPrompt inner = new(expectedPrompt, expectedMetadata, expectedResult);
7122

72-
TestDelegatingMcpServerPrompt delegating = new(mock.Object);
23+
TestDelegatingPrompt delegating = new(inner);
7324

74-
Assert.Equal("inner-prompt-string", delegating.ToString());
25+
Assert.Same(expectedPrompt, delegating.ProtocolPrompt);
26+
Assert.Same(expectedMetadata, delegating.Metadata);
27+
Assert.Same(expectedResult, await delegating.GetAsync(null!, CancellationToken.None));
28+
Assert.Equal(inner.ToString(), delegating.ToString());
7529
}
7630

7731
[Fact]
@@ -87,7 +41,6 @@ public void OverridesAllVirtualAndAbstractMembers()
8741
{
8842
MethodInfo? overriddenMethod = typeof(DelegatingMcpServerPrompt).GetMethod(
8943
baseMethod.Name,
90-
BindingFlags.Public | BindingFlags.Instance,
9144
baseMethod.GetParameters().Select(p => p.ParameterType).ToArray());
9245

9346
Assert.True(
@@ -96,5 +49,13 @@ public void OverridesAllVirtualAndAbstractMembers()
9649
}
9750
}
9851

99-
private sealed class TestDelegatingMcpServerPrompt(McpServerPrompt innerPrompt) : DelegatingMcpServerPrompt(innerPrompt);
52+
private sealed class TestDelegatingPrompt(McpServerPrompt innerPrompt) : DelegatingMcpServerPrompt(innerPrompt);
53+
54+
private sealed class InnerPrompt(Prompt protocolPrompt, IReadOnlyList<object> metadata, GetPromptResult result) : McpServerPrompt
55+
{
56+
public override Prompt ProtocolPrompt => protocolPrompt;
57+
public override IReadOnlyList<object> Metadata => metadata;
58+
public override ValueTask<GetPromptResult> GetAsync(RequestContext<GetPromptRequestParams> request, CancellationToken cancellationToken = default) => new(result);
59+
public override string ToString() => "inner-prompt";
60+
}
10061
}
Lines changed: 24 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,34 @@
11
using ModelContextProtocol.Protocol;
22
using ModelContextProtocol.Server;
3-
using Moq;
43
using System.Reflection;
54

65
namespace ModelContextProtocol.Tests.Server;
76

87
public class DelegatingMcpServerResourceTests
98
{
10-
private static JsonRpcRequest CreateTestJsonRpcRequest() => new()
11-
{
12-
Id = new RequestId("test-id"),
13-
Method = "test/method",
14-
Params = null,
15-
};
16-
179
[Fact]
1810
public void Ctor_NullInnerResource_Throws()
1911
{
20-
Assert.Throws<ArgumentNullException>("innerResource", () => new TestDelegatingMcpServerResource(null!));
21-
}
22-
23-
[Fact]
24-
public void ProtocolResourceTemplate_DelegatesToInnerResource()
25-
{
26-
ResourceTemplate expected = new() { Name = "test-resource", UriTemplate = "test://resource" };
27-
Mock<McpServerResource> mock = new();
28-
mock.Setup(r => r.ProtocolResourceTemplate).Returns(expected);
29-
30-
TestDelegatingMcpServerResource delegating = new(mock.Object);
31-
32-
Assert.Same(expected, delegating.ProtocolResourceTemplate);
33-
mock.Verify(r => r.ProtocolResourceTemplate, Times.Once);
34-
}
35-
36-
[Fact]
37-
public void ProtocolResource_DelegatesToInnerResource()
38-
{
39-
Resource expected = new() { Name = "test-resource", Uri = "test://resource" };
40-
Mock<McpServerResource> mock = new();
41-
mock.Setup(r => r.ProtocolResource).Returns(expected);
42-
43-
TestDelegatingMcpServerResource delegating = new(mock.Object);
44-
45-
Assert.Same(expected, delegating.ProtocolResource);
46-
mock.Verify(r => r.ProtocolResource, Times.Once);
47-
}
48-
49-
[Fact]
50-
public void Metadata_DelegatesToInnerResource()
51-
{
52-
IReadOnlyList<object> expected = new object[] { "attr1", "attr2" };
53-
Mock<McpServerResource> mock = new();
54-
mock.Setup(r => r.Metadata).Returns(expected);
55-
56-
TestDelegatingMcpServerResource delegating = new(mock.Object);
57-
58-
Assert.Same(expected, delegating.Metadata);
59-
mock.Verify(r => r.Metadata, Times.Once);
12+
Assert.Throws<ArgumentNullException>("innerResource", () => new TestDelegatingResource(null!));
6013
}
6114

6215
[Fact]
63-
public void IsMatch_DelegatesToInnerResource()
16+
public async Task AllMembers_DelegateToInnerResource()
6417
{
65-
Mock<McpServerResource> mock = new();
66-
mock.Setup(r => r.IsMatch("test://resource")).Returns(true);
18+
ResourceTemplate expectedTemplate = new() { Name = "sentinel-resource", UriTemplate = "test://resource" };
19+
Resource expectedResource = new() { Name = "sentinel-resource", Uri = "test://resource" };
20+
IReadOnlyList<object> expectedMetadata = new object[] { "m1" };
21+
ReadResourceResult expectedResult = new() { Contents = [] };
22+
InnerResource inner = new(expectedTemplate, expectedResource, expectedMetadata, expectedResult);
6723

68-
TestDelegatingMcpServerResource delegating = new(mock.Object);
24+
TestDelegatingResource delegating = new(inner);
6925

26+
Assert.Same(expectedTemplate, delegating.ProtocolResourceTemplate);
27+
Assert.Same(expectedResource, delegating.ProtocolResource);
28+
Assert.Same(expectedMetadata, delegating.Metadata);
7029
Assert.True(delegating.IsMatch("test://resource"));
71-
mock.Verify(r => r.IsMatch("test://resource"), Times.Once);
72-
}
73-
74-
[Fact]
75-
public async Task ReadAsync_DelegatesToInnerResource()
76-
{
77-
ReadResourceResult expected = new() { Contents = [] };
78-
RequestContext<ReadResourceRequestParams> request = new(new Mock<McpServer>().Object, CreateTestJsonRpcRequest());
79-
using CancellationTokenSource cts = new();
80-
Mock<McpServerResource> mock = new();
81-
mock.Setup(r => r.ReadAsync(request, cts.Token)).ReturnsAsync(expected);
82-
83-
TestDelegatingMcpServerResource delegating = new(mock.Object);
84-
85-
ReadResourceResult result = await delegating.ReadAsync(request, cts.Token);
86-
87-
Assert.Same(expected, result);
88-
mock.Verify(r => r.ReadAsync(request, cts.Token), Times.Once);
89-
}
90-
91-
[Fact]
92-
public void ToString_DelegatesToInnerResource()
93-
{
94-
Mock<McpServerResource> mock = new();
95-
mock.Setup(r => r.ToString()).Returns("inner-resource-string");
96-
97-
TestDelegatingMcpServerResource delegating = new(mock.Object);
98-
99-
Assert.Equal("inner-resource-string", delegating.ToString());
30+
Assert.Same(expectedResult, await delegating.ReadAsync(null!, CancellationToken.None));
31+
Assert.Equal(inner.ToString(), delegating.ToString());
10032
}
10133

10234
[Fact]
@@ -112,7 +44,6 @@ public void OverridesAllVirtualAndAbstractMembers()
11244
{
11345
MethodInfo? overriddenMethod = typeof(DelegatingMcpServerResource).GetMethod(
11446
baseMethod.Name,
115-
BindingFlags.Public | BindingFlags.Instance,
11647
baseMethod.GetParameters().Select(p => p.ParameterType).ToArray());
11748

11849
Assert.True(
@@ -121,5 +52,15 @@ public void OverridesAllVirtualAndAbstractMembers()
12152
}
12253
}
12354

124-
private sealed class TestDelegatingMcpServerResource(McpServerResource innerResource) : DelegatingMcpServerResource(innerResource);
55+
private sealed class TestDelegatingResource(McpServerResource innerResource) : DelegatingMcpServerResource(innerResource);
56+
57+
private sealed class InnerResource(ResourceTemplate protocolResourceTemplate, Resource protocolResource, IReadOnlyList<object> metadata, ReadResourceResult result) : McpServerResource
58+
{
59+
public override ResourceTemplate ProtocolResourceTemplate => protocolResourceTemplate;
60+
public override Resource? ProtocolResource => protocolResource;
61+
public override IReadOnlyList<object> Metadata => metadata;
62+
public override bool IsMatch(string uri) => true;
63+
public override ValueTask<ReadResourceResult> ReadAsync(RequestContext<ReadResourceRequestParams> request, CancellationToken cancellationToken = default) => new(result);
64+
public override string ToString() => "inner-resource";
65+
}
12566
}
Lines changed: 20 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,31 @@
11
using ModelContextProtocol.Protocol;
22
using ModelContextProtocol.Server;
3-
using Moq;
43
using System.Reflection;
54

65
namespace ModelContextProtocol.Tests.Server;
76

87
public class DelegatingMcpServerToolTests
98
{
10-
private static JsonRpcRequest CreateTestJsonRpcRequest() => new()
11-
{
12-
Id = new RequestId("test-id"),
13-
Method = "test/method",
14-
Params = null,
15-
};
16-
179
[Fact]
1810
public void Ctor_NullInnerTool_Throws()
1911
{
20-
Assert.Throws<ArgumentNullException>("innerTool", () => new TestDelegatingMcpServerTool(null!));
21-
}
22-
23-
[Fact]
24-
public void ProtocolTool_DelegatesToInnerTool()
25-
{
26-
Tool expected = new() { Name = "test-tool" };
27-
Mock<McpServerTool> mock = new();
28-
mock.Setup(t => t.ProtocolTool).Returns(expected);
29-
30-
TestDelegatingMcpServerTool delegating = new(mock.Object);
31-
32-
Assert.Same(expected, delegating.ProtocolTool);
33-
mock.Verify(t => t.ProtocolTool, Times.Once);
34-
}
35-
36-
[Fact]
37-
public void Metadata_DelegatesToInnerTool()
38-
{
39-
IReadOnlyList<object> expected = new object[] { "attr1", "attr2" };
40-
Mock<McpServerTool> mock = new();
41-
mock.Setup(t => t.Metadata).Returns(expected);
42-
43-
TestDelegatingMcpServerTool delegating = new(mock.Object);
44-
45-
Assert.Same(expected, delegating.Metadata);
46-
mock.Verify(t => t.Metadata, Times.Once);
47-
}
48-
49-
[Fact]
50-
public async Task InvokeAsync_DelegatesToInnerTool()
51-
{
52-
CallToolResult expected = new() { Content = [] };
53-
RequestContext<CallToolRequestParams> request = new(new Mock<McpServer>().Object, CreateTestJsonRpcRequest());
54-
using CancellationTokenSource cts = new();
55-
Mock<McpServerTool> mock = new();
56-
mock.Setup(t => t.InvokeAsync(request, cts.Token)).ReturnsAsync(expected);
57-
58-
TestDelegatingMcpServerTool delegating = new(mock.Object);
59-
60-
CallToolResult result = await delegating.InvokeAsync(request, cts.Token);
61-
62-
Assert.Same(expected, result);
63-
mock.Verify(t => t.InvokeAsync(request, cts.Token), Times.Once);
12+
Assert.Throws<ArgumentNullException>("innerTool", () => new TestDelegatingTool(null!));
6413
}
6514

6615
[Fact]
67-
public void ToString_DelegatesToInnerTool()
16+
public async Task AllMembers_DelegateToInnerTool()
6817
{
69-
Mock<McpServerTool> mock = new();
70-
mock.Setup(t => t.ToString()).Returns("inner-tool-string");
18+
Tool expectedTool = new() { Name = "sentinel-tool" };
19+
IReadOnlyList<object> expectedMetadata = new object[] { "m1" };
20+
CallToolResult expectedResult = new() { Content = [] };
21+
InnerTool inner = new(expectedTool, expectedMetadata, expectedResult);
7122

72-
TestDelegatingMcpServerTool delegating = new(mock.Object);
23+
TestDelegatingTool delegating = new(inner);
7324

74-
Assert.Equal("inner-tool-string", delegating.ToString());
25+
Assert.Same(expectedTool, delegating.ProtocolTool);
26+
Assert.Same(expectedMetadata, delegating.Metadata);
27+
Assert.Same(expectedResult, await delegating.InvokeAsync(null!, CancellationToken.None));
28+
Assert.Equal(inner.ToString(), delegating.ToString());
7529
}
7630

7731
[Fact]
@@ -87,7 +41,6 @@ public void OverridesAllVirtualAndAbstractMembers()
8741
{
8842
MethodInfo? overriddenMethod = typeof(DelegatingMcpServerTool).GetMethod(
8943
baseMethod.Name,
90-
BindingFlags.Public | BindingFlags.Instance,
9144
baseMethod.GetParameters().Select(p => p.ParameterType).ToArray());
9245

9346
Assert.True(
@@ -96,5 +49,13 @@ public void OverridesAllVirtualAndAbstractMembers()
9649
}
9750
}
9851

99-
private sealed class TestDelegatingMcpServerTool(McpServerTool innerTool) : DelegatingMcpServerTool(innerTool);
52+
private sealed class TestDelegatingTool(McpServerTool innerTool) : DelegatingMcpServerTool(innerTool);
53+
54+
private sealed class InnerTool(Tool protocolTool, IReadOnlyList<object> metadata, CallToolResult result) : McpServerTool
55+
{
56+
public override Tool ProtocolTool => protocolTool;
57+
public override IReadOnlyList<object> Metadata => metadata;
58+
public override ValueTask<CallToolResult> InvokeAsync(RequestContext<CallToolRequestParams> request, CancellationToken cancellationToken = default) => new(result);
59+
public override string ToString() => "inner-tool";
60+
}
10061
}

0 commit comments

Comments
 (0)