Skip to content

Commit bc53b01

Browse files
Add comprehensive tests for JsonRpcMessage.Converter optimization
- Test all message types: Request, Notification, Response, Error - Test with various ID types (string, number) - Test null result handling - Test error handling for invalid messages - Test round-trip serialization/deserialization - Test unknown property handling - All 16 new tests pass Co-authored-by: eiriktsarpalis <2813363+eiriktsarpalis@users.noreply.github.com>
1 parent 9f01d2d commit bc53b01

1 file changed

Lines changed: 305 additions & 0 deletions

File tree

Lines changed: 305 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,305 @@
1+
using ModelContextProtocol.Protocol;
2+
using System.Text.Json;
3+
using System.Text.Json.Nodes;
4+
5+
namespace ModelContextProtocol.Tests.Protocol;
6+
7+
/// <summary>
8+
/// Tests for the optimized JsonRpcMessage.Converter implementation.
9+
/// </summary>
10+
public static class JsonRpcMessageConverterTests
11+
{
12+
[Fact]
13+
public static void Deserialize_JsonRpcRequest_WithAllProperties()
14+
{
15+
// Arrange
16+
string json = """{"jsonrpc":"2.0","id":123,"method":"test/method","params":{"key":"value"}}""";
17+
18+
// Act
19+
var message = JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions);
20+
21+
// Assert
22+
Assert.NotNull(message);
23+
Assert.IsType<JsonRpcRequest>(message);
24+
var request = (JsonRpcRequest)message;
25+
Assert.Equal("2.0", request.JsonRpc);
26+
Assert.Equal(new RequestId(123), request.Id);
27+
Assert.Equal("test/method", request.Method);
28+
Assert.NotNull(request.Params);
29+
Assert.Equal("value", request.Params["key"]?.GetValue<string>());
30+
}
31+
32+
[Fact]
33+
public static void Deserialize_JsonRpcRequest_WithStringId()
34+
{
35+
// Arrange
36+
string json = """{"jsonrpc":"2.0","id":"abc-123","method":"test/method"}""";
37+
38+
// Act
39+
var message = JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions);
40+
41+
// Assert
42+
Assert.NotNull(message);
43+
Assert.IsType<JsonRpcRequest>(message);
44+
var request = (JsonRpcRequest)message;
45+
Assert.Equal("2.0", request.JsonRpc);
46+
Assert.Equal(new RequestId("abc-123"), request.Id);
47+
Assert.Equal("test/method", request.Method);
48+
}
49+
50+
[Fact]
51+
public static void Deserialize_JsonRpcNotification_WithParams()
52+
{
53+
// Arrange
54+
string json = """{"jsonrpc":"2.0","method":"notifications/progress","params":{"progress":50}}""";
55+
56+
// Act
57+
var message = JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions);
58+
59+
// Assert
60+
Assert.NotNull(message);
61+
Assert.IsType<JsonRpcNotification>(message);
62+
var notification = (JsonRpcNotification)message;
63+
Assert.Equal("2.0", notification.JsonRpc);
64+
Assert.Equal("notifications/progress", notification.Method);
65+
Assert.NotNull(notification.Params);
66+
Assert.Equal(50, notification.Params["progress"]?.GetValue<int>());
67+
}
68+
69+
[Fact]
70+
public static void Deserialize_JsonRpcResponse_WithResult()
71+
{
72+
// Arrange
73+
string json = """{"jsonrpc":"2.0","id":42,"result":{"status":"success","data":[1,2,3]}}""";
74+
75+
// Act
76+
var message = JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions);
77+
78+
// Assert
79+
Assert.NotNull(message);
80+
Assert.IsType<JsonRpcResponse>(message);
81+
var response = (JsonRpcResponse)message;
82+
Assert.Equal("2.0", response.JsonRpc);
83+
Assert.Equal(new RequestId(42), response.Id);
84+
Assert.NotNull(response.Result);
85+
Assert.Equal("success", response.Result["status"]?.GetValue<string>());
86+
}
87+
88+
[Fact]
89+
public static void Deserialize_JsonRpcResponse_WithNullResult()
90+
{
91+
// Arrange
92+
string json = """{"jsonrpc":"2.0","id":1,"result":null}""";
93+
94+
// Act
95+
var message = JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions);
96+
97+
// Assert
98+
Assert.NotNull(message);
99+
Assert.IsType<JsonRpcResponse>(message);
100+
var response = (JsonRpcResponse)message;
101+
Assert.Equal("2.0", response.JsonRpc);
102+
Assert.Equal(new RequestId(1), response.Id);
103+
Assert.Null(response.Result);
104+
}
105+
106+
[Fact]
107+
public static void Deserialize_JsonRpcError_WithErrorDetails()
108+
{
109+
// Arrange
110+
string json = """{"jsonrpc":"2.0","id":"req-1","error":{"code":-32600,"message":"Invalid Request","data":"Additional error info"}}""";
111+
112+
// Act
113+
var message = JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions);
114+
115+
// Assert
116+
Assert.NotNull(message);
117+
Assert.IsType<JsonRpcError>(message);
118+
var error = (JsonRpcError)message;
119+
Assert.Equal("2.0", error.JsonRpc);
120+
Assert.Equal(new RequestId("req-1"), error.Id);
121+
Assert.NotNull(error.Error);
122+
Assert.Equal(-32600, error.Error.Code);
123+
Assert.Equal("Invalid Request", error.Error.Message);
124+
Assert.Equal("Additional error info", error.Error.Data?.ToString());
125+
}
126+
127+
[Fact]
128+
public static void Deserialize_JsonRpcMessage_IgnoresUnknownProperties()
129+
{
130+
// Arrange - JSON with unknown properties
131+
string json = """{"jsonrpc":"2.0","id":1,"method":"test","params":{},"extra":"ignored","another":123}""";
132+
133+
// Act
134+
var message = JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions);
135+
136+
// Assert - should successfully deserialize, ignoring unknown properties
137+
Assert.NotNull(message);
138+
Assert.IsType<JsonRpcRequest>(message);
139+
var request = (JsonRpcRequest)message;
140+
Assert.Equal("test", request.Method);
141+
}
142+
143+
[Fact]
144+
public static void Deserialize_InvalidJsonRpcVersion_ThrowsException()
145+
{
146+
// Arrange
147+
string json = """{"jsonrpc":"1.0","id":1,"method":"test"}""";
148+
149+
// Act & Assert
150+
var exception = Assert.Throws<JsonException>(() =>
151+
JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions));
152+
Assert.Contains("jsonrpc version", exception.Message);
153+
}
154+
155+
[Fact]
156+
public static void Deserialize_MissingJsonRpcVersion_ThrowsException()
157+
{
158+
// Arrange
159+
string json = """{"id":1,"method":"test"}""";
160+
161+
// Act & Assert
162+
var exception = Assert.Throws<JsonException>(() =>
163+
JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions));
164+
Assert.Contains("jsonrpc version", exception.Message);
165+
}
166+
167+
[Fact]
168+
public static void Deserialize_ResponseWithoutResultOrError_ThrowsException()
169+
{
170+
// Arrange
171+
string json = """{"jsonrpc":"2.0","id":1}""";
172+
173+
// Act & Assert
174+
var exception = Assert.Throws<JsonException>(() =>
175+
JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions));
176+
Assert.Contains("result or error", exception.Message);
177+
}
178+
179+
[Fact]
180+
public static void Deserialize_InvalidMessageFormat_ThrowsException()
181+
{
182+
// Arrange - neither request nor response nor notification
183+
string json = """{"jsonrpc":"2.0"}""";
184+
185+
// Act & Assert
186+
var exception = Assert.Throws<JsonException>(() =>
187+
JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions));
188+
Assert.Contains("Invalid JSON-RPC message format", exception.Message);
189+
}
190+
191+
[Fact]
192+
public static void Serialize_JsonRpcRequest_ProducesCorrectJson()
193+
{
194+
// Arrange
195+
var request = new JsonRpcRequest
196+
{
197+
JsonRpc = "2.0",
198+
Id = new RequestId(123),
199+
Method = "test/method",
200+
Params = JsonNode.Parse("""{"key":"value"}""")
201+
};
202+
203+
// Act
204+
string json = JsonSerializer.Serialize<JsonRpcMessage>(request, McpJsonUtilities.DefaultOptions);
205+
206+
// Assert
207+
Assert.Contains("\"jsonrpc\":\"2.0\"", json);
208+
Assert.Contains("\"id\":123", json);
209+
Assert.Contains("\"method\":\"test/method\"", json);
210+
Assert.Contains("\"key\":\"value\"", json);
211+
}
212+
213+
[Fact]
214+
public static void Serialize_JsonRpcNotification_ProducesCorrectJson()
215+
{
216+
// Arrange
217+
var notification = new JsonRpcNotification
218+
{
219+
JsonRpc = "2.0",
220+
Method = "notifications/test"
221+
};
222+
223+
// Act
224+
string json = JsonSerializer.Serialize<JsonRpcMessage>(notification, McpJsonUtilities.DefaultOptions);
225+
226+
// Assert
227+
Assert.Contains("\"jsonrpc\":\"2.0\"", json);
228+
Assert.Contains("\"method\":\"notifications/test\"", json);
229+
Assert.DoesNotContain("\"id\"", json);
230+
}
231+
232+
[Fact]
233+
public static void RoundTrip_Request_PreservesData()
234+
{
235+
// Arrange
236+
var original = new JsonRpcRequest
237+
{
238+
JsonRpc = "2.0",
239+
Id = new RequestId("test-id"),
240+
Method = "some/method",
241+
Params = JsonNode.Parse("""{"nested":{"value":42}}""")
242+
};
243+
244+
// Act
245+
string json = JsonSerializer.Serialize<JsonRpcMessage>(original, McpJsonUtilities.DefaultOptions);
246+
var deserialized = JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions) as JsonRpcRequest;
247+
248+
// Assert
249+
Assert.NotNull(deserialized);
250+
Assert.Equal(original.JsonRpc, deserialized.JsonRpc);
251+
Assert.Equal(original.Id, deserialized.Id);
252+
Assert.Equal(original.Method, deserialized.Method);
253+
Assert.Equal(42, deserialized.Params?["nested"]?["value"]?.GetValue<int>());
254+
}
255+
256+
[Fact]
257+
public static void RoundTrip_Response_PreservesData()
258+
{
259+
// Arrange
260+
var original = new JsonRpcResponse
261+
{
262+
JsonRpc = "2.0",
263+
Id = new RequestId(999),
264+
Result = JsonNode.Parse("""{"success":true,"items":[1,2,3]}""")
265+
};
266+
267+
// Act
268+
string json = JsonSerializer.Serialize<JsonRpcMessage>(original, McpJsonUtilities.DefaultOptions);
269+
var deserialized = JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions) as JsonRpcResponse;
270+
271+
// Assert
272+
Assert.NotNull(deserialized);
273+
Assert.Equal(original.JsonRpc, deserialized.JsonRpc);
274+
Assert.Equal(original.Id, deserialized.Id);
275+
Assert.True(deserialized.Result?["success"]?.GetValue<bool>());
276+
}
277+
278+
[Fact]
279+
public static void RoundTrip_Error_PreservesData()
280+
{
281+
// Arrange
282+
var original = new JsonRpcError
283+
{
284+
JsonRpc = "2.0",
285+
Id = new RequestId(100),
286+
Error = new JsonRpcErrorDetail
287+
{
288+
Code = -32601,
289+
Message = "Method not found",
290+
Data = "test/unknown"
291+
}
292+
};
293+
294+
// Act
295+
string json = JsonSerializer.Serialize<JsonRpcMessage>(original, McpJsonUtilities.DefaultOptions);
296+
var deserialized = JsonSerializer.Deserialize<JsonRpcMessage>(json, McpJsonUtilities.DefaultOptions) as JsonRpcError;
297+
298+
// Assert
299+
Assert.NotNull(deserialized);
300+
Assert.Equal(original.JsonRpc, deserialized.JsonRpc);
301+
Assert.Equal(original.Id, deserialized.Id);
302+
Assert.Equal(original.Error.Code, deserialized.Error.Code);
303+
Assert.Equal(original.Error.Message, deserialized.Error.Message);
304+
}
305+
}

0 commit comments

Comments
 (0)