
* This adjusts the `Request`/`Response` types to have an `id` that is either a string or a number. * Merges 'Error' into 'Response' to have a single response type that represents both errors and results. * Adjusts the `Error.data` field to by any JSON value. * Adds `operator==` support to the base protocol types and simplifies the tests.
307 lines
10 KiB
C++
307 lines
10 KiB
C++
//===-- 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 "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<Request> 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<Response> 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<Notification> 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<ToolCapability> 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<Capabilities> 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<TextContent> 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<TextResult> 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<ToolDefinition> 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<Message> deserialized_message = roundtripJSON(message);
|
|
ASSERT_THAT_EXPECTED(deserialized_message, llvm::Succeeded());
|
|
|
|
ASSERT_TRUE(std::holds_alternative<Request>(*deserialized_message));
|
|
const Request &deserialized_request =
|
|
std::get<Request>(*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<Message> deserialized_message = roundtripJSON(message);
|
|
ASSERT_THAT_EXPECTED(deserialized_message, llvm::Succeeded());
|
|
|
|
ASSERT_TRUE(std::holds_alternative<Response>(*deserialized_message));
|
|
const Response &deserialized_response =
|
|
std::get<Response>(*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<Message> deserialized_message = roundtripJSON(message);
|
|
ASSERT_THAT_EXPECTED(deserialized_message, llvm::Succeeded());
|
|
|
|
ASSERT_TRUE(std::holds_alternative<Notification>(*deserialized_message));
|
|
const Notification &deserialized_notification =
|
|
std::get<Notification>(*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<Message> deserialized_message = roundtripJSON(message);
|
|
ASSERT_THAT_EXPECTED(deserialized_message, llvm::Succeeded());
|
|
|
|
ASSERT_TRUE(std::holds_alternative<Response>(*deserialized_message));
|
|
const Response &deserialized_error =
|
|
std::get<Response>(*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<Resource> 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<Resource> 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<ResourceContents> 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<ResourceContents> 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<ResourceResult> 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<ResourceResult> deserialized_result = roundtripJSON(result);
|
|
ASSERT_THAT_EXPECTED(deserialized_result, llvm::Succeeded());
|
|
|
|
EXPECT_TRUE(deserialized_result->contents.empty());
|
|
}
|