cpp-mcp/test/test_mcp_resource.cpp

262 lines
9.1 KiB
C++

/**
* @file test_mcp_resource.cpp
* @brief Test MCP resource related functionality
*
* This file contains unit tests for the MCP resource module, based on the 2024-11-05 specification.
*/
#include "mcp_resource.h"
#include "mcp_server.h"
#include "mcp_client.h"
#include "base64.hpp"
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <string>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <thread>
#include <chrono>
// Create a test directory and file
class ResourceTest : public ::testing::Test {
protected:
void SetUp() override {
// Create test directory
test_dir = std::filesystem::temp_directory_path() / "mcp_test";
std::filesystem::create_directories(test_dir);
// Create test file
test_file = test_dir / "test.txt";
std::ofstream file(test_file);
file << "Test file content";
file.close();
// Output test directory and file path for debugging
std::cout << "Test directory: " << test_dir << std::endl;
std::cout << "Test file: " << test_file << std::endl;
// Ensure file exists
ASSERT_TRUE(std::filesystem::exists(test_file)) << "Test file was not created successfully";
}
void TearDown() override {
// Clean up test directory
std::filesystem::remove_all(test_dir);
}
std::filesystem::path test_dir;
std::filesystem::path test_file;
};
// Test text resource
TEST(McpResourceTest, TextResourceTest) {
// Create text resource
mcp::text_resource resource("test://example.txt", "example.txt", "text/plain", "Example text resource");
// Test metadata
mcp::json metadata = resource.get_metadata();
EXPECT_EQ(metadata["uri"], "test://example.txt");
EXPECT_EQ(metadata["name"], "example.txt");
EXPECT_EQ(metadata["mimeType"], "text/plain");
EXPECT_EQ(metadata["description"], "Example text resource");
// Test URI
EXPECT_EQ(resource.get_uri(), "test://example.txt");
// Test setting and getting text
std::string test_content = "This is a test content";
resource.set_text(test_content);
EXPECT_EQ(resource.get_text(), test_content);
// Test read
mcp::json content = resource.read();
EXPECT_EQ(content["uri"], "test://example.txt");
EXPECT_EQ(content["mimeType"], "text/plain");
EXPECT_EQ(content["text"], test_content);
// Test modification
EXPECT_FALSE(resource.is_modified());
resource.set_text("New content");
EXPECT_TRUE(resource.is_modified());
// Test read after modification
content = resource.read();
EXPECT_EQ(content["text"], "New content");
EXPECT_FALSE(resource.is_modified());
}
// Test binary resource
TEST(McpResourceTest, BinaryResourceTest) {
// Create binary resource
mcp::binary_resource resource("test://example.bin", "example.bin", "application/octet-stream", "Example binary resource");
// Test metadata
mcp::json metadata = resource.get_metadata();
EXPECT_EQ(metadata["uri"], "test://example.bin");
EXPECT_EQ(metadata["name"], "example.bin");
EXPECT_EQ(metadata["mimeType"], "application/octet-stream");
EXPECT_EQ(metadata["description"], "Example binary resource");
// Test URI
EXPECT_EQ(resource.get_uri(), "test://example.bin");
// Test setting and getting binary data
std::vector<uint8_t> test_data = {0x48, 0x65, 0x6C, 0x6C, 0x6F}; // "Hello" in ASCII
resource.set_data(test_data.data(), test_data.size());
const auto& data = resource.get_data();
EXPECT_EQ(data.size(), test_data.size());
EXPECT_TRUE(std::equal(data.begin(), data.end(), test_data.begin()));
// Test read
mcp::json content = resource.read();
EXPECT_EQ(content["uri"], "test://example.bin");
EXPECT_EQ(content["mimeType"], "application/octet-stream");
// Base64 encode the data for comparison
std::string base64_data = base64::encode(reinterpret_cast<const char*>(test_data.data()), test_data.size());
EXPECT_EQ(content["blob"], base64_data);
// Test modification
EXPECT_FALSE(resource.is_modified());
std::vector<uint8_t> new_data = {0x57, 0x6F, 0x72, 0x6C, 0x64}; // "World" in ASCII
resource.set_data(new_data.data(), new_data.size());
EXPECT_TRUE(resource.is_modified());
// Test read after modification
content = resource.read();
std::string new_base64_data = base64::encode(reinterpret_cast<const char*>(new_data.data()), new_data.size());
EXPECT_EQ(content["blob"], new_base64_data);
EXPECT_FALSE(resource.is_modified());
}
// Test file resource
TEST_F(ResourceTest, FileResourceTest) {
// Create file resource
mcp::file_resource resource(test_file.string());
// Test metadata
mcp::json metadata = resource.get_metadata();
EXPECT_EQ(metadata["uri"], "file://" + test_file.string());
EXPECT_EQ(metadata["name"], test_file.filename().string());
EXPECT_EQ(metadata["mimeType"], "text/plain");
// Test URI
EXPECT_EQ(resource.get_uri(), "file://" + test_file.string());
// Test read
mcp::json content = resource.read();
EXPECT_EQ(content["uri"], "file://" + test_file.string());
EXPECT_EQ(content["mimeType"], "text/plain");
EXPECT_EQ(content["text"], "Test file content");
// Test modification detection
EXPECT_FALSE(resource.is_modified());
// Modify file
{
std::ofstream file(test_file);
file << "Modified content";
file.close();
}
// Test modification detection after file change
EXPECT_TRUE(resource.is_modified());
// Test read after modification
content = resource.read();
EXPECT_EQ(content["text"], "Modified content");
EXPECT_FALSE(resource.is_modified());
// Test file deletion detection
std::filesystem::remove(test_file);
EXPECT_TRUE(resource.is_modified());
}
// Test resource manager
TEST_F(ResourceTest, ResourceManagerTest) {
// Get resource manager instance
mcp::resource_manager& manager = mcp::resource_manager::instance();
// Create resources
auto text_res = std::make_shared<mcp::text_resource>("test://text.txt", "text.txt", "text/plain", "Text resource");
auto binary_res = std::make_shared<mcp::binary_resource>("test://binary.bin", "binary.bin", "application/octet-stream", "Binary resource");
auto file_res = std::make_shared<mcp::file_resource>(test_file.string());
// Set content
text_res->set_text("Text resource content");
std::vector<uint8_t> binary_data = {0x42, 0x69, 0x6E, 0x61, 0x72, 0x79}; // "Binary" in ASCII
binary_res->set_data(binary_data.data(), binary_data.size());
// Register resources
manager.register_resource(text_res);
manager.register_resource(binary_res);
manager.register_resource(file_res);
// Test list resources
mcp::json resources_list = manager.list_resources();
EXPECT_EQ(resources_list["resources"].size(), 3);
// Test get resource
auto retrieved_text_res = manager.get_resource("test://text.txt");
ASSERT_NE(retrieved_text_res, nullptr);
EXPECT_EQ(retrieved_text_res->get_uri(), "test://text.txt");
auto retrieved_binary_res = manager.get_resource("test://binary.bin");
ASSERT_NE(retrieved_binary_res, nullptr);
EXPECT_EQ(retrieved_binary_res->get_uri(), "test://binary.bin");
auto retrieved_file_res = manager.get_resource("file://" + test_file.string());
ASSERT_NE(retrieved_file_res, nullptr);
EXPECT_EQ(retrieved_file_res->get_uri(), "file://" + test_file.string());
// Test unregister resource
EXPECT_TRUE(manager.unregister_resource("test://text.txt"));
EXPECT_EQ(manager.get_resource("test://text.txt"), nullptr);
// Test resources list after unregister
resources_list = manager.list_resources();
EXPECT_EQ(resources_list["resources"].size(), 2);
// Test subscription
bool notification_received = false;
std::string notification_uri;
int subscription_id = manager.subscribe("test://binary.bin", [&](const std::string& uri) {
notification_received = true;
notification_uri = uri;
});
// Modify resource and notify
binary_res->set_data(binary_data.data(), binary_data.size()); // This sets modified flag
manager.notify_resource_changed("test://binary.bin");
// Check notification
EXPECT_TRUE(notification_received);
EXPECT_EQ(notification_uri, "test://binary.bin");
// Test unsubscribe
EXPECT_TRUE(manager.unsubscribe(subscription_id));
// Reset notification flags
notification_received = false;
notification_uri = "";
// Modify and notify again
binary_res->set_data(binary_data.data(), binary_data.size());
manager.notify_resource_changed("test://binary.bin");
// Check no notification after unsubscribe
EXPECT_FALSE(notification_received);
EXPECT_EQ(notification_uri, "");
// Clean up
manager.unregister_resource("test://binary.bin");
manager.unregister_resource("file://" + test_file.string());
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}