1//===-- ProtocolMCPTest.cpp -----------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Plugins/Protocol/MCP/Protocol.h"
10#include "TestingSupport/TestUtilities.h"
11#include "llvm/Testing/Support/Error.h"
12#include "gtest/gtest.h"
13
14using namespace lldb;
15using namespace lldb_private;
16using namespace lldb_private::mcp::protocol;
17
18TEST(ProtocolMCPTest, Request) {
19 Request request;
20 request.id = 1;
21 request.method = "foo";
22 request.params = llvm::json::Object{{.K: "key", .V: "value"}};
23
24 llvm::Expected<Request> deserialized_request = roundtripJSON(input: request);
25 ASSERT_THAT_EXPECTED(deserialized_request, llvm::Succeeded());
26
27 EXPECT_EQ(request.id, deserialized_request->id);
28 EXPECT_EQ(request.method, deserialized_request->method);
29 EXPECT_EQ(request.params, deserialized_request->params);
30}
31
32TEST(ProtocolMCPTest, Response) {
33 Response response;
34 response.id = 1;
35 response.result = llvm::json::Object{{.K: "key", .V: "value"}};
36
37 llvm::Expected<Response> deserialized_response = roundtripJSON(input: response);
38 ASSERT_THAT_EXPECTED(deserialized_response, llvm::Succeeded());
39
40 EXPECT_EQ(response.id, deserialized_response->id);
41 EXPECT_EQ(response.result, deserialized_response->result);
42}
43
44TEST(ProtocolMCPTest, Notification) {
45 Notification notification;
46 notification.method = "notifyMethod";
47 notification.params = llvm::json::Object{{.K: "key", .V: "value"}};
48
49 llvm::Expected<Notification> deserialized_notification =
50 roundtripJSON(input: notification);
51 ASSERT_THAT_EXPECTED(deserialized_notification, llvm::Succeeded());
52
53 EXPECT_EQ(notification.method, deserialized_notification->method);
54 EXPECT_EQ(notification.params, deserialized_notification->params);
55}
56
57TEST(ProtocolMCPTest, ToolCapability) {
58 ToolCapability tool_capability;
59 tool_capability.listChanged = true;
60
61 llvm::Expected<ToolCapability> deserialized_tool_capability =
62 roundtripJSON(input: tool_capability);
63 ASSERT_THAT_EXPECTED(deserialized_tool_capability, llvm::Succeeded());
64
65 EXPECT_EQ(tool_capability.listChanged,
66 deserialized_tool_capability->listChanged);
67}
68
69TEST(ProtocolMCPTest, Capabilities) {
70 ToolCapability tool_capability;
71 tool_capability.listChanged = true;
72
73 Capabilities capabilities;
74 capabilities.tools = tool_capability;
75
76 llvm::Expected<Capabilities> deserialized_capabilities =
77 roundtripJSON(input: capabilities);
78 ASSERT_THAT_EXPECTED(deserialized_capabilities, llvm::Succeeded());
79
80 EXPECT_EQ(capabilities.tools.listChanged,
81 deserialized_capabilities->tools.listChanged);
82}
83
84TEST(ProtocolMCPTest, TextContent) {
85 TextContent text_content;
86 text_content.text = "Sample text";
87
88 llvm::Expected<TextContent> deserialized_text_content =
89 roundtripJSON(input: text_content);
90 ASSERT_THAT_EXPECTED(deserialized_text_content, llvm::Succeeded());
91
92 EXPECT_EQ(text_content.text, deserialized_text_content->text);
93}
94
95TEST(ProtocolMCPTest, TextResult) {
96 TextContent text_content1;
97 text_content1.text = "Text 1";
98
99 TextContent text_content2;
100 text_content2.text = "Text 2";
101
102 TextResult text_result;
103 text_result.content = {text_content1, text_content2};
104 text_result.isError = true;
105
106 llvm::Expected<TextResult> deserialized_text_result =
107 roundtripJSON(input: text_result);
108 ASSERT_THAT_EXPECTED(deserialized_text_result, llvm::Succeeded());
109
110 EXPECT_EQ(text_result.isError, deserialized_text_result->isError);
111 ASSERT_EQ(text_result.content.size(),
112 deserialized_text_result->content.size());
113 EXPECT_EQ(text_result.content[0].text,
114 deserialized_text_result->content[0].text);
115 EXPECT_EQ(text_result.content[1].text,
116 deserialized_text_result->content[1].text);
117}
118
119TEST(ProtocolMCPTest, ToolDefinition) {
120 ToolDefinition tool_definition;
121 tool_definition.name = "ToolName";
122 tool_definition.description = "Tool Description";
123 tool_definition.inputSchema =
124 llvm::json::Object{{.K: "schemaKey", .V: "schemaValue"}};
125
126 llvm::Expected<ToolDefinition> deserialized_tool_definition =
127 roundtripJSON(input: tool_definition);
128 ASSERT_THAT_EXPECTED(deserialized_tool_definition, llvm::Succeeded());
129
130 EXPECT_EQ(tool_definition.name, deserialized_tool_definition->name);
131 EXPECT_EQ(tool_definition.description,
132 deserialized_tool_definition->description);
133 EXPECT_EQ(tool_definition.inputSchema,
134 deserialized_tool_definition->inputSchema);
135}
136
137TEST(ProtocolMCPTest, MessageWithRequest) {
138 Request request;
139 request.id = 1;
140 request.method = "test_method";
141 request.params = llvm::json::Object{{.K: "param", .V: "value"}};
142
143 Message message = request;
144
145 llvm::Expected<Message> deserialized_message = roundtripJSON(input: message);
146 ASSERT_THAT_EXPECTED(deserialized_message, llvm::Succeeded());
147
148 ASSERT_TRUE(std::holds_alternative<Request>(*deserialized_message));
149 const Request &deserialized_request =
150 std::get<Request>(v&: *deserialized_message);
151
152 EXPECT_EQ(request.id, deserialized_request.id);
153 EXPECT_EQ(request.method, deserialized_request.method);
154 EXPECT_EQ(request.params, deserialized_request.params);
155}
156
157TEST(ProtocolMCPTest, MessageWithResponse) {
158 Response response;
159 response.id = 2;
160 response.result = llvm::json::Object{{.K: "result", .V: "success"}};
161
162 Message message = response;
163
164 llvm::Expected<Message> deserialized_message = roundtripJSON(input: message);
165 ASSERT_THAT_EXPECTED(deserialized_message, llvm::Succeeded());
166
167 ASSERT_TRUE(std::holds_alternative<Response>(*deserialized_message));
168 const Response &deserialized_response =
169 std::get<Response>(v&: *deserialized_message);
170
171 EXPECT_EQ(response.id, deserialized_response.id);
172 EXPECT_EQ(response.result, deserialized_response.result);
173}
174
175TEST(ProtocolMCPTest, MessageWithNotification) {
176 Notification notification;
177 notification.method = "notification_method";
178 notification.params = llvm::json::Object{{.K: "notify", .V: "data"}};
179
180 Message message = notification;
181
182 llvm::Expected<Message> deserialized_message = roundtripJSON(input: message);
183 ASSERT_THAT_EXPECTED(deserialized_message, llvm::Succeeded());
184
185 ASSERT_TRUE(std::holds_alternative<Notification>(*deserialized_message));
186 const Notification &deserialized_notification =
187 std::get<Notification>(v&: *deserialized_message);
188
189 EXPECT_EQ(notification.method, deserialized_notification.method);
190 EXPECT_EQ(notification.params, deserialized_notification.params);
191}
192
193TEST(ProtocolMCPTest, MessageWithError) {
194 ErrorInfo error_info;
195 error_info.code = -32603;
196 error_info.message = "Internal error";
197
198 Error error;
199 error.id = 3;
200 error.error = error_info;
201
202 Message message = error;
203
204 llvm::Expected<Message> deserialized_message = roundtripJSON(input: message);
205 ASSERT_THAT_EXPECTED(deserialized_message, llvm::Succeeded());
206
207 ASSERT_TRUE(std::holds_alternative<Error>(*deserialized_message));
208 const Error &deserialized_error = std::get<Error>(v&: *deserialized_message);
209
210 EXPECT_EQ(error.id, deserialized_error.id);
211 EXPECT_EQ(error.error.code, deserialized_error.error.code);
212 EXPECT_EQ(error.error.message, deserialized_error.error.message);
213}
214
215TEST(ProtocolMCPTest, ResponseWithError) {
216 ErrorInfo error_info;
217 error_info.code = -32700;
218 error_info.message = "Parse error";
219
220 Response response;
221 response.id = 4;
222 response.error = error_info;
223
224 llvm::Expected<Response> deserialized_response = roundtripJSON(input: response);
225 ASSERT_THAT_EXPECTED(deserialized_response, llvm::Succeeded());
226
227 EXPECT_EQ(response.id, deserialized_response->id);
228 EXPECT_FALSE(deserialized_response->result.has_value());
229 ASSERT_TRUE(deserialized_response->error.has_value());
230 EXPECT_EQ(response.error->code, deserialized_response->error->code);
231 EXPECT_EQ(response.error->message, deserialized_response->error->message);
232}
233
234TEST(ProtocolMCPTest, Resource) {
235 Resource resource;
236 resource.uri = "resource://example/test";
237 resource.name = "Test Resource";
238 resource.description = "A test resource for unit testing";
239 resource.mimeType = "text/plain";
240
241 llvm::Expected<Resource> deserialized_resource = roundtripJSON(input: resource);
242 ASSERT_THAT_EXPECTED(deserialized_resource, llvm::Succeeded());
243
244 EXPECT_EQ(resource.uri, deserialized_resource->uri);
245 EXPECT_EQ(resource.name, deserialized_resource->name);
246 EXPECT_EQ(resource.description, deserialized_resource->description);
247 EXPECT_EQ(resource.mimeType, deserialized_resource->mimeType);
248}
249
250TEST(ProtocolMCPTest, ResourceWithoutOptionals) {
251 Resource resource;
252 resource.uri = "resource://example/minimal";
253 resource.name = "Minimal Resource";
254
255 llvm::Expected<Resource> deserialized_resource = roundtripJSON(input: resource);
256 ASSERT_THAT_EXPECTED(deserialized_resource, llvm::Succeeded());
257
258 EXPECT_EQ(resource.uri, deserialized_resource->uri);
259 EXPECT_EQ(resource.name, deserialized_resource->name);
260 EXPECT_TRUE(deserialized_resource->description.empty());
261 EXPECT_TRUE(deserialized_resource->mimeType.empty());
262}
263
264TEST(ProtocolMCPTest, ResourceContents) {
265 ResourceContents contents;
266 contents.uri = "resource://example/content";
267 contents.text = "This is the content of the resource";
268 contents.mimeType = "text/plain";
269
270 llvm::Expected<ResourceContents> deserialized_contents =
271 roundtripJSON(input: contents);
272 ASSERT_THAT_EXPECTED(deserialized_contents, llvm::Succeeded());
273
274 EXPECT_EQ(contents.uri, deserialized_contents->uri);
275 EXPECT_EQ(contents.text, deserialized_contents->text);
276 EXPECT_EQ(contents.mimeType, deserialized_contents->mimeType);
277}
278
279TEST(ProtocolMCPTest, ResourceContentsWithoutMimeType) {
280 ResourceContents contents;
281 contents.uri = "resource://example/content-no-mime";
282 contents.text = "Content without mime type specified";
283
284 llvm::Expected<ResourceContents> deserialized_contents =
285 roundtripJSON(input: contents);
286 ASSERT_THAT_EXPECTED(deserialized_contents, llvm::Succeeded());
287
288 EXPECT_EQ(contents.uri, deserialized_contents->uri);
289 EXPECT_EQ(contents.text, deserialized_contents->text);
290 EXPECT_TRUE(deserialized_contents->mimeType.empty());
291}
292
293TEST(ProtocolMCPTest, ResourceResult) {
294 ResourceContents contents1;
295 contents1.uri = "resource://example/content1";
296 contents1.text = "First resource content";
297 contents1.mimeType = "text/plain";
298
299 ResourceContents contents2;
300 contents2.uri = "resource://example/content2";
301 contents2.text = "Second resource content";
302 contents2.mimeType = "application/json";
303
304 ResourceResult result;
305 result.contents = {contents1, contents2};
306
307 llvm::Expected<ResourceResult> deserialized_result = roundtripJSON(input: result);
308 ASSERT_THAT_EXPECTED(deserialized_result, llvm::Succeeded());
309
310 ASSERT_EQ(result.contents.size(), deserialized_result->contents.size());
311
312 EXPECT_EQ(result.contents[0].uri, deserialized_result->contents[0].uri);
313 EXPECT_EQ(result.contents[0].text, deserialized_result->contents[0].text);
314 EXPECT_EQ(result.contents[0].mimeType,
315 deserialized_result->contents[0].mimeType);
316
317 EXPECT_EQ(result.contents[1].uri, deserialized_result->contents[1].uri);
318 EXPECT_EQ(result.contents[1].text, deserialized_result->contents[1].text);
319 EXPECT_EQ(result.contents[1].mimeType,
320 deserialized_result->contents[1].mimeType);
321}
322
323TEST(ProtocolMCPTest, ResourceResultEmpty) {
324 ResourceResult result;
325
326 llvm::Expected<ResourceResult> deserialized_result = roundtripJSON(input: result);
327 ASSERT_THAT_EXPECTED(deserialized_result, llvm::Succeeded());
328
329 EXPECT_TRUE(deserialized_result->contents.empty());
330}
331

source code of lldb/unittests/Protocol/ProtocolMCPTest.cpp