//===-- 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 "ProtocolMCPTestUtilities.h" #include "TestingSupport/TestUtilities.h" #include "lldb/Protocol/MCP/Protocol.h" #include "llvm/Testing/Support/Error.h" #include "gtest/gtest.h" using namespace lldb; using namespace lldb_private; using namespace lldb_protocol::mcp; 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, ServerCapabilities) { ServerCapabilities capabilities; capabilities.supportsToolsList = true; llvm::Expected deserialized_capabilities = roundtripJSON(capabilities); ASSERT_THAT_EXPECTED(deserialized_capabilities, llvm::Succeeded()); EXPECT_EQ(capabilities.supportsToolsList, deserialized_capabilities->supportsToolsList); } 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, CallToolResult) { TextContent text_content1; text_content1.text = "Text 1"; TextContent text_content2; text_content2.text = "Text 2"; CallToolResult 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, deserialized_request); } 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, deserialized_response); } 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, deserialized_notification); } TEST(ProtocolMCPTest, MessageWithErrorResponse) { Error error; error.code = -32603; error.message = "Internal error"; Response error_response; error_response.id = 3; error_response.result = error; Message message = error_response; llvm::Expected deserialized_message = roundtripJSON(message); ASSERT_THAT_EXPECTED(deserialized_message, llvm::Succeeded()); ASSERT_TRUE(std::holds_alternative(*deserialized_message)); const Response &deserialized_error = std::get(*deserialized_message); EXPECT_EQ(error_response, deserialized_error); } 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, TextResourceContents) { TextResourceContents 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, TextResourceContentsWithoutMimeType) { TextResourceContents 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, ReadResourceResult) { TextResourceContents contents1; contents1.uri = "resource://example/content1"; contents1.text = "First resource content"; contents1.mimeType = "text/plain"; TextResourceContents contents2; contents2.uri = "resource://example/content2"; contents2.text = "Second resource content"; contents2.mimeType = "application/json"; ReadResourceResult 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, ReadResourceResultEmpty) { ReadResourceResult result; llvm::Expected deserialized_result = roundtripJSON(result); ASSERT_THAT_EXPECTED(deserialized_result, llvm::Succeeded()); EXPECT_TRUE(deserialized_result->contents.empty()); }