//===-- ProtocolMCPTest.cpp -----------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "Plugins/Protocol/MCP/Protocol.h" #include "TestingSupport/TestUtilities.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" using namespace lldb; using namespace lldb_private; using namespace lldb_private::mcp::protocol; TEST(ProtocolMCPTest, Request) { Request request; request.id = 1; request.method = "foo"; request.params = llvm::json::Object{{"key", "value"}}; llvm::Expected deserialized_request = roundtripJSON(request); ASSERT_THAT_EXPECTED(deserialized_request, llvm::Succeeded()); EXPECT_EQ(request.id, deserialized_request->id); EXPECT_EQ(request.method, deserialized_request->method); EXPECT_EQ(request.params, deserialized_request->params); } TEST(ProtocolMCPTest, Response) { Response response; response.id = 1; response.result = llvm::json::Object{{"key", "value"}}; llvm::Expected deserialized_response = roundtripJSON(response); ASSERT_THAT_EXPECTED(deserialized_response, llvm::Succeeded()); EXPECT_EQ(response.id, deserialized_response->id); EXPECT_EQ(response.result, deserialized_response->result); } TEST(ProtocolMCPTest, Notification) { Notification notification; notification.method = "notifyMethod"; notification.params = llvm::json::Object{{"key", "value"}}; llvm::Expected deserialized_notification = roundtripJSON(notification); ASSERT_THAT_EXPECTED(deserialized_notification, llvm::Succeeded()); EXPECT_EQ(notification.method, deserialized_notification->method); EXPECT_EQ(notification.params, deserialized_notification->params); } TEST(ProtocolMCPTest, ToolCapability) { ToolCapability tool_capability; tool_capability.listChanged = true; llvm::Expected deserialized_tool_capability = roundtripJSON(tool_capability); ASSERT_THAT_EXPECTED(deserialized_tool_capability, llvm::Succeeded()); EXPECT_EQ(tool_capability.listChanged, deserialized_tool_capability->listChanged); } TEST(ProtocolMCPTest, Capabilities) { ToolCapability tool_capability; tool_capability.listChanged = true; Capabilities capabilities; capabilities.tools = tool_capability; llvm::Expected deserialized_capabilities = roundtripJSON(capabilities); ASSERT_THAT_EXPECTED(deserialized_capabilities, llvm::Succeeded()); EXPECT_EQ(capabilities.tools.listChanged, deserialized_capabilities->tools.listChanged); } TEST(ProtocolMCPTest, TextContent) { TextContent text_content; text_content.text = "Sample text"; llvm::Expected deserialized_text_content = roundtripJSON(text_content); ASSERT_THAT_EXPECTED(deserialized_text_content, llvm::Succeeded()); EXPECT_EQ(text_content.text, deserialized_text_content->text); } TEST(ProtocolMCPTest, TextResult) { TextContent text_content1; text_content1.text = "Text 1"; TextContent text_content2; text_content2.text = "Text 2"; TextResult text_result; text_result.content = {text_content1, text_content2}; text_result.isError = true; llvm::Expected deserialized_text_result = roundtripJSON(text_result); ASSERT_THAT_EXPECTED(deserialized_text_result, llvm::Succeeded()); EXPECT_EQ(text_result.isError, deserialized_text_result->isError); ASSERT_EQ(text_result.content.size(), deserialized_text_result->content.size()); EXPECT_EQ(text_result.content[0].text, deserialized_text_result->content[0].text); EXPECT_EQ(text_result.content[1].text, deserialized_text_result->content[1].text); } TEST(ProtocolMCPTest, ToolDefinition) { ToolDefinition tool_definition; tool_definition.name = "ToolName"; tool_definition.description = "Tool Description"; tool_definition.inputSchema = llvm::json::Object{{"schemaKey", "schemaValue"}}; llvm::Expected deserialized_tool_definition = roundtripJSON(tool_definition); ASSERT_THAT_EXPECTED(deserialized_tool_definition, llvm::Succeeded()); EXPECT_EQ(tool_definition.name, deserialized_tool_definition->name); EXPECT_EQ(tool_definition.description, deserialized_tool_definition->description); EXPECT_EQ(tool_definition.inputSchema, deserialized_tool_definition->inputSchema); } TEST(ProtocolMCPTest, MessageWithRequest) { Request request; request.id = 1; request.method = "test_method"; request.params = llvm::json::Object{{"param", "value"}}; Message message = request; llvm::Expected deserialized_message = roundtripJSON(message); ASSERT_THAT_EXPECTED(deserialized_message, llvm::Succeeded()); ASSERT_TRUE(std::holds_alternative(*deserialized_message)); const Request &deserialized_request = std::get(*deserialized_message); EXPECT_EQ(request.id, deserialized_request.id); EXPECT_EQ(request.method, deserialized_request.method); EXPECT_EQ(request.params, deserialized_request.params); } TEST(ProtocolMCPTest, MessageWithResponse) { Response response; response.id = 2; response.result = llvm::json::Object{{"result", "success"}}; Message message = response; llvm::Expected deserialized_message = roundtripJSON(message); ASSERT_THAT_EXPECTED(deserialized_message, llvm::Succeeded()); ASSERT_TRUE(std::holds_alternative(*deserialized_message)); const Response &deserialized_response = std::get(*deserialized_message); EXPECT_EQ(response.id, deserialized_response.id); EXPECT_EQ(response.result, deserialized_response.result); } TEST(ProtocolMCPTest, MessageWithNotification) { Notification notification; notification.method = "notification_method"; notification.params = llvm::json::Object{{"notify", "data"}}; Message message = notification; llvm::Expected deserialized_message = roundtripJSON(message); ASSERT_THAT_EXPECTED(deserialized_message, llvm::Succeeded()); ASSERT_TRUE(std::holds_alternative(*deserialized_message)); const Notification &deserialized_notification = std::get(*deserialized_message); EXPECT_EQ(notification.method, deserialized_notification.method); EXPECT_EQ(notification.params, deserialized_notification.params); } TEST(ProtocolMCPTest, MessageWithError) { ErrorInfo error_info; error_info.code = -32603; error_info.message = "Internal error"; Error error; error.id = 3; error.error = error_info; Message message = error; llvm::Expected deserialized_message = roundtripJSON(message); ASSERT_THAT_EXPECTED(deserialized_message, llvm::Succeeded()); ASSERT_TRUE(std::holds_alternative(*deserialized_message)); const Error &deserialized_error = std::get(*deserialized_message); EXPECT_EQ(error.id, deserialized_error.id); EXPECT_EQ(error.error.code, deserialized_error.error.code); EXPECT_EQ(error.error.message, deserialized_error.error.message); } TEST(ProtocolMCPTest, ResponseWithError) { ErrorInfo error_info; error_info.code = -32700; error_info.message = "Parse error"; Response response; response.id = 4; response.error = error_info; llvm::Expected deserialized_response = roundtripJSON(response); ASSERT_THAT_EXPECTED(deserialized_response, llvm::Succeeded()); EXPECT_EQ(response.id, deserialized_response->id); EXPECT_FALSE(deserialized_response->result.has_value()); ASSERT_TRUE(deserialized_response->error.has_value()); EXPECT_EQ(response.error->code, deserialized_response->error->code); EXPECT_EQ(response.error->message, deserialized_response->error->message); } TEST(ProtocolMCPTest, Resource) { Resource resource; resource.uri = "resource://example/test"; resource.name = "Test Resource"; resource.description = "A test resource for unit testing"; resource.mimeType = "text/plain"; llvm::Expected deserialized_resource = roundtripJSON(resource); ASSERT_THAT_EXPECTED(deserialized_resource, llvm::Succeeded()); EXPECT_EQ(resource.uri, deserialized_resource->uri); EXPECT_EQ(resource.name, deserialized_resource->name); EXPECT_EQ(resource.description, deserialized_resource->description); EXPECT_EQ(resource.mimeType, deserialized_resource->mimeType); } TEST(ProtocolMCPTest, ResourceWithoutOptionals) { Resource resource; resource.uri = "resource://example/minimal"; resource.name = "Minimal Resource"; llvm::Expected deserialized_resource = roundtripJSON(resource); ASSERT_THAT_EXPECTED(deserialized_resource, llvm::Succeeded()); EXPECT_EQ(resource.uri, deserialized_resource->uri); EXPECT_EQ(resource.name, deserialized_resource->name); EXPECT_TRUE(deserialized_resource->description.empty()); EXPECT_TRUE(deserialized_resource->mimeType.empty()); } TEST(ProtocolMCPTest, ResourceContents) { ResourceContents contents; contents.uri = "resource://example/content"; contents.text = "This is the content of the resource"; contents.mimeType = "text/plain"; llvm::Expected deserialized_contents = roundtripJSON(contents); ASSERT_THAT_EXPECTED(deserialized_contents, llvm::Succeeded()); EXPECT_EQ(contents.uri, deserialized_contents->uri); EXPECT_EQ(contents.text, deserialized_contents->text); EXPECT_EQ(contents.mimeType, deserialized_contents->mimeType); } TEST(ProtocolMCPTest, ResourceContentsWithoutMimeType) { ResourceContents contents; contents.uri = "resource://example/content-no-mime"; contents.text = "Content without mime type specified"; llvm::Expected deserialized_contents = roundtripJSON(contents); ASSERT_THAT_EXPECTED(deserialized_contents, llvm::Succeeded()); EXPECT_EQ(contents.uri, deserialized_contents->uri); EXPECT_EQ(contents.text, deserialized_contents->text); EXPECT_TRUE(deserialized_contents->mimeType.empty()); } TEST(ProtocolMCPTest, ResourceResult) { ResourceContents contents1; contents1.uri = "resource://example/content1"; contents1.text = "First resource content"; contents1.mimeType = "text/plain"; ResourceContents contents2; contents2.uri = "resource://example/content2"; contents2.text = "Second resource content"; contents2.mimeType = "application/json"; ResourceResult result; result.contents = {contents1, contents2}; llvm::Expected deserialized_result = roundtripJSON(result); ASSERT_THAT_EXPECTED(deserialized_result, llvm::Succeeded()); ASSERT_EQ(result.contents.size(), deserialized_result->contents.size()); EXPECT_EQ(result.contents[0].uri, deserialized_result->contents[0].uri); EXPECT_EQ(result.contents[0].text, deserialized_result->contents[0].text); EXPECT_EQ(result.contents[0].mimeType, deserialized_result->contents[0].mimeType); EXPECT_EQ(result.contents[1].uri, deserialized_result->contents[1].uri); EXPECT_EQ(result.contents[1].text, deserialized_result->contents[1].text); EXPECT_EQ(result.contents[1].mimeType, deserialized_result->contents[1].mimeType); } TEST(ProtocolMCPTest, ResourceResultEmpty) { ResourceResult result; llvm::Expected deserialized_result = roundtripJSON(result); ASSERT_THAT_EXPECTED(deserialized_result, llvm::Succeeded()); EXPECT_TRUE(deserialized_result->contents.empty()); }