blob: 029b422c19dd3fdd85e82d41b23ca9b4dc8838f1 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "runtime/plugin/cloud_plugin_downloader.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <filesystem>
#include <fstream>
#include "cloud/cloud_storage_engine.h"
#include "runtime/exec_env.h"
#include "storage/storage_engine.h"
namespace doris {
class CloudPluginDownloaderTest : public ::testing::Test {
protected:
void SetUp() override { downloader = std::make_unique<CloudPluginDownloader>(); }
void TearDown() override {
downloader.reset();
ExecEnv::GetInstance()->set_storage_engine(nullptr);
}
void SetupRegularStorageEngine() {
ExecEnv::GetInstance()->set_storage_engine(
std::make_unique<StorageEngine>(EngineOptions()));
}
void SetupCloudStorageEngine() {
ExecEnv::GetInstance()->set_storage_engine(
std::make_unique<CloudStorageEngine>(EngineOptions()));
}
std::unique_ptr<CloudPluginDownloader> downloader;
};
// ============== Core Business Logic Tests ==============
TEST_F(CloudPluginDownloaderTest, TestBuildPluginPath) {
std::string path;
Status status = downloader->_build_plugin_path(CloudPluginDownloader::PluginType::JDBC_DRIVERS,
"mysql-connector.jar", &path);
EXPECT_TRUE(status.ok());
EXPECT_EQ("plugins/jdbc_drivers/mysql-connector.jar", path);
status = downloader->_build_plugin_path(CloudPluginDownloader::PluginType::JAVA_UDF,
"my-udf.jar", &path);
EXPECT_TRUE(status.ok());
EXPECT_EQ("plugins/java_udf/my-udf.jar", path);
}
TEST_F(CloudPluginDownloaderTest, TestBuildPluginPathEdgeCases) {
std::string path;
Status status = downloader->_build_plugin_path(CloudPluginDownloader::PluginType::JDBC_DRIVERS,
"test-file_v1.2.jar", &path);
EXPECT_TRUE(status.ok());
EXPECT_EQ("plugins/jdbc_drivers/test-file_v1.2.jar", path);
status = downloader->_build_plugin_path(CloudPluginDownloader::PluginType::JAVA_UDF,
"sub/dir/file.jar", &path);
EXPECT_TRUE(status.ok());
EXPECT_EQ("plugins/java_udf/sub/dir/file.jar", path);
std::string long_name(100, 'a');
long_name += ".jar";
status = downloader->_build_plugin_path(CloudPluginDownloader::PluginType::JDBC_DRIVERS,
long_name, &path);
EXPECT_TRUE(status.ok());
std::string expected = "plugins/jdbc_drivers/" + long_name;
EXPECT_EQ(expected, path);
}
TEST_F(CloudPluginDownloaderTest, TestBuildPluginPathUnsupportedTypes) {
std::string path;
Status status = downloader->_build_plugin_path(CloudPluginDownloader::PluginType::CONNECTORS,
"kafka-connector.jar", &path);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), ErrorCode::INVALID_ARGUMENT);
EXPECT_THAT(status.msg(), testing::HasSubstr("Unsupported plugin type"));
status = downloader->_build_plugin_path(CloudPluginDownloader::PluginType::HADOOP_CONF,
"core-site.xml", &path);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), ErrorCode::INVALID_ARGUMENT);
EXPECT_THAT(status.msg(), testing::HasSubstr("Unsupported plugin type"));
}
TEST_F(CloudPluginDownloaderTest, TestBuildPluginPathInvalidType) {
CloudPluginDownloader::PluginType invalid_type =
static_cast<CloudPluginDownloader::PluginType>(999);
std::string path;
Status status = downloader->_build_plugin_path(invalid_type, "test.jar", &path);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), ErrorCode::INVALID_ARGUMENT);
EXPECT_THAT(status.msg(), testing::HasSubstr("Unsupported plugin type"));
}
// ============== Storage Engine Integration Tests ==============
TEST_F(CloudPluginDownloaderTest, TestDownloadFromCloudEmptyName) {
std::string result_path;
Status status = CloudPluginDownloader::download_from_cloud(
CloudPluginDownloader::PluginType::JDBC_DRIVERS, "", "/tmp/test.jar", &result_path);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), ErrorCode::INVALID_ARGUMENT);
EXPECT_EQ("Plugin name cannot be empty", status.msg());
}
TEST_F(CloudPluginDownloaderTest, TestDownloadFromCloudRegularStorageEngine) {
SetupRegularStorageEngine();
std::string result_path;
Status status = CloudPluginDownloader::download_from_cloud(
CloudPluginDownloader::PluginType::JDBC_DRIVERS, "mysql.jar", "/tmp/test.jar",
&result_path);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), ErrorCode::NOT_FOUND);
EXPECT_TRUE(status.to_string().find("CloudStorageEngine not found") != std::string::npos);
}
TEST_F(CloudPluginDownloaderTest, TestGetCloudFilesystemNonCloudEnvironment) {
SetupRegularStorageEngine();
io::RemoteFileSystemSPtr filesystem;
Status status = downloader->_get_cloud_filesystem(&filesystem);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), ErrorCode::NOT_FOUND);
EXPECT_TRUE(status.to_string().find("CloudStorageEngine not found") != std::string::npos);
}
TEST_F(CloudPluginDownloaderTest, TestGetCloudFilesystemNoStorageEngine) {
SetupRegularStorageEngine();
io::RemoteFileSystemSPtr filesystem;
Status status = downloader->_get_cloud_filesystem(&filesystem);
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), ErrorCode::NOT_FOUND);
}
TEST_F(CloudPluginDownloaderTest, TestGetCloudFilesystemCloudEnvironment) {
SetupCloudStorageEngine();
io::RemoteFileSystemSPtr filesystem;
Status status = downloader->_get_cloud_filesystem(&filesystem);
if (!status.ok()) {
EXPECT_EQ(status.code(), ErrorCode::NOT_FOUND);
EXPECT_TRUE(status.to_string().find("No latest filesystem available") != std::string::npos);
}
}
TEST_F(CloudPluginDownloaderTest, TestDownloadFromCloudCloudStorageEngine) {
SetupCloudStorageEngine();
std::string result_path;
Status status = CloudPluginDownloader::download_from_cloud(
CloudPluginDownloader::PluginType::JDBC_DRIVERS, "mysql.jar", "/tmp/test.jar",
&result_path);
EXPECT_FALSE(status.ok());
}
// ============== File System Tests ==============
TEST_F(CloudPluginDownloaderTest, TestPrepareLocalPathSuccess) {
std::string test_path = "/tmp/test_new_file.jar";
Status status = downloader->_prepare_local_path(test_path);
EXPECT_TRUE(status.ok());
}
TEST_F(CloudPluginDownloaderTest, TestPrepareLocalPathWithExistingFile) {
std::string existing_file = "/tmp/existing_file.jar";
std::ofstream file(existing_file);
file << "existing content";
file.close();
EXPECT_TRUE(std::filesystem::exists(existing_file));
Status status = downloader->_prepare_local_path(existing_file);
EXPECT_TRUE(status.ok());
EXPECT_FALSE(std::filesystem::exists(existing_file));
}
TEST_F(CloudPluginDownloaderTest, TestPrepareLocalPathWithNestedDirectory) {
std::string nested_path = "/tmp/test_dir/nested/test.jar";
Status status = downloader->_prepare_local_path(nested_path);
EXPECT_TRUE(status.ok());
EXPECT_TRUE(std::filesystem::exists("/tmp/test_dir/nested"));
std::filesystem::remove_all("/tmp/test_dir");
}
TEST_F(CloudPluginDownloaderTest, TestPrepareLocalPathRootDirectory) {
std::string root_path = "/test_root.jar";
Status status = downloader->_prepare_local_path(root_path);
EXPECT_TRUE(status.ok());
std::filesystem::remove(root_path);
}
// ============== Consistency Tests ==============
TEST_F(CloudPluginDownloaderTest, TestAllTypePathCombinations) {
struct TestCase {
CloudPluginDownloader::PluginType type;
std::string name;
std::string expected;
};
std::vector<TestCase> test_cases = {
{CloudPluginDownloader::PluginType::JDBC_DRIVERS, "driver.jar",
"plugins/jdbc_drivers/driver.jar"},
{CloudPluginDownloader::PluginType::JAVA_UDF, "udf.jar", "plugins/java_udf/udf.jar"},
};
for (const auto& test_case : test_cases) {
std::string result;
Status status = downloader->_build_plugin_path(test_case.type, test_case.name, &result);
EXPECT_TRUE(status.ok()) << "Failed for type: " << static_cast<int>(test_case.type)
<< ", name: " << test_case.name << ", error: " << status.msg();
EXPECT_EQ(test_case.expected, result)
<< "Failed for type: " << static_cast<int>(test_case.type)
<< ", name: " << test_case.name;
}
}
} // namespace doris