Skip to content

Commit 2f5ffc6

Browse files
committed
Add disposal tests
1 parent 6fbbdd5 commit 2f5ffc6

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

tests/ModelContextProtocol.Tests/Server/McpServerToolTests.cs

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Moq;
55
using System.Reflection;
66
using System.Text.Json;
7+
using System.Text.Json.Serialization;
78

89
namespace ModelContextProtocol.Tests.Server;
910

@@ -89,5 +90,120 @@ public async Task SupportsOptionalServiceFromDI()
8990
Assert.Equal("42", result.Content[0].Text);
9091
}
9192

93+
[Fact]
94+
public async Task SupportsDisposingInstantiatedDisposableTargets()
95+
{
96+
McpServerTool tool1 = McpServerTool.Create(
97+
typeof(DisposableToolType).GetMethod(nameof(DisposableToolType.InstanceMethod))!,
98+
typeof(DisposableToolType));
99+
100+
var result = await tool1.InvokeAsync(
101+
new RequestContext<CallToolRequestParams>(null!, null),
102+
TestContext.Current.CancellationToken);
103+
Assert.Equal("""{"disposals":1}""", result.Content[0].Text);
104+
}
105+
106+
[Fact]
107+
public async Task SupportsAsyncDisposingInstantiatedAsyncDisposableTargets()
108+
{
109+
McpServerTool tool1 = McpServerTool.Create(
110+
typeof(AsyncDisposableToolType).GetMethod(nameof(AsyncDisposableToolType.InstanceMethod))!,
111+
typeof(AsyncDisposableToolType));
112+
113+
var result = await tool1.InvokeAsync(
114+
new RequestContext<CallToolRequestParams>(null!, null),
115+
TestContext.Current.CancellationToken);
116+
Assert.Equal("""{"asyncDisposals":1}""", result.Content[0].Text);
117+
}
118+
119+
[Fact]
120+
public async Task SupportsAsyncDisposingInstantiatedAsyncDisposableAndDisposableTargets()
121+
{
122+
McpServerTool tool1 = McpServerTool.Create(
123+
typeof(AsyncDisposableAndDisposableToolType).GetMethod(nameof(AsyncDisposableAndDisposableToolType.InstanceMethod))!,
124+
typeof(AsyncDisposableAndDisposableToolType));
125+
126+
var result = await tool1.InvokeAsync(
127+
new RequestContext<CallToolRequestParams>(null!, null),
128+
TestContext.Current.CancellationToken);
129+
Assert.Equal("""{"asyncDisposals":1,"disposals":0}""", result.Content[0].Text);
130+
}
131+
92132
private sealed class MyService;
133+
134+
private class DisposableToolType : IDisposable
135+
{
136+
public int Disposals { get; private set; }
137+
138+
public void Dispose()
139+
{
140+
Disposals++;
141+
}
142+
143+
public object InstanceMethod()
144+
{
145+
if (Disposals != 0)
146+
{
147+
throw new InvalidOperationException("Dispose was called");
148+
}
149+
150+
return this;
151+
}
152+
}
153+
154+
private class AsyncDisposableToolType : IAsyncDisposable
155+
{
156+
public int AsyncDisposals { get; private set; }
157+
158+
public ValueTask DisposeAsync()
159+
{
160+
AsyncDisposals++;
161+
return default;
162+
}
163+
164+
public object InstanceMethod()
165+
{
166+
if (AsyncDisposals != 0)
167+
{
168+
throw new InvalidOperationException("DisposeAsync was called");
169+
}
170+
171+
return this;
172+
}
173+
}
174+
175+
private class AsyncDisposableAndDisposableToolType : IAsyncDisposable, IDisposable
176+
{
177+
[JsonPropertyOrder(0)]
178+
public int AsyncDisposals { get; private set; }
179+
180+
[JsonPropertyOrder(1)]
181+
public int Disposals { get; private set; }
182+
183+
public void Dispose()
184+
{
185+
Disposals++;
186+
}
187+
188+
public ValueTask DisposeAsync()
189+
{
190+
AsyncDisposals++;
191+
return default;
192+
}
193+
194+
public object InstanceMethod()
195+
{
196+
if (Disposals != 0)
197+
{
198+
throw new InvalidOperationException("Dispose was called");
199+
}
200+
201+
if (AsyncDisposals != 0)
202+
{
203+
throw new InvalidOperationException("DisposeAsync was called");
204+
}
205+
206+
return this;
207+
}
208+
}
93209
}

0 commit comments

Comments
 (0)