/** * @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 #include #include #include #include #include #include #include // 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 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(test_data.data()), test_data.size()); EXPECT_EQ(content["blob"], base64_data); // Test modification EXPECT_FALSE(resource.is_modified()); std::vector 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(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("test://text.txt", "text.txt", "text/plain", "Text resource"); auto binary_res = std::make_shared("test://binary.bin", "binary.bin", "application/octet-stream", "Binary resource"); auto file_res = std::make_shared(test_file.string()); // Set content text_res->set_text("Text resource content"); std::vector 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(); }