Skip to content

Commit

Permalink
feat(centralizedConfiguration): added a wrapper for the filesystem fu…
Browse files Browse the repository at this point in the history
…nctions and corrected the tests
  • Loading branch information
Nicogp committed Dec 4, 2024
1 parent bd69dcf commit c824964
Show file tree
Hide file tree
Showing 10 changed files with 297 additions and 37 deletions.
2 changes: 1 addition & 1 deletion src/agent/centralized_configuration/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ find_package(nlohmann_json REQUIRED)

add_library(CentralizedConfiguration src/centralized_configuration.cpp)
target_include_directories(CentralizedConfiguration PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(CentralizedConfiguration PUBLIC ModuleCommand Boost::asio nlohmann_json::nlohmann_json Config PRIVATE Logger)
target_link_libraries(CentralizedConfiguration PUBLIC ModuleCommand Boost::asio nlohmann_json::nlohmann_json Config FilesystemWrapper PRIVATE Logger)

include(../../cmake/ConfigureTarget.cmake)
configure_target(CentralizedConfiguration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

#include <nlohmann/json.hpp>

#include <filesystem_wrapper.hpp>
#include <functional>
#include <ifilesystem.hpp>
#include <string>
#include <vector>

Expand All @@ -23,6 +25,14 @@ namespace centralized_configuration
using ValidateFileFunctionType = std::function<bool(const std::filesystem::path& configFile)>;
using ReloadModulesFunctionType = std::function<void()>;

/// @brief Constructor that allows injecting a file system wrapper.
/// @param fileSystemWrapper An optional filesystem wrapper. If nullptr, it will use FileSystemWrapper
explicit CentralizedConfiguration(std::shared_ptr<IFileSystem> fileSystemWrapper = nullptr)
: m_fileSystemWrapper(fileSystemWrapper ? fileSystemWrapper
: std::make_shared<filesystem_wrapper::FileSystemWrapper>())
{
}

/// @brief Executes a command for the centralized configuration system.
/// @param command A string containing a JSON command to execute.
/// @param parameters A json object containing the parameters of the command to be executed.
Expand Down Expand Up @@ -58,6 +68,9 @@ namespace centralized_configuration
void ReloadModulesFunction(ReloadModulesFunctionType reloadModulesFunction);

private:
/// @brief Member to interact with the file system.
std::shared_ptr<IFileSystem> m_fileSystemWrapper;

/// @brief Function to set group IDs.
SetGroupIdFunctionType m_setGroupIdFunction;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,12 @@ namespace centralized_configuration
{
std::filesystem::path sharedDirPath(config::DEFAULT_SHARED_CONFIG_PATH);

if (std::filesystem::exists(sharedDirPath) && std::filesystem::is_directory(sharedDirPath))
if (m_fileSystemWrapper->exists(sharedDirPath) &&
m_fileSystemWrapper->is_directory(sharedDirPath))
{
for (const auto& entry : std::filesystem::directory_iterator(sharedDirPath))
{
std::filesystem::remove_all(entry);
m_fileSystemWrapper->remove_all(entry);
}
}
}
Expand Down Expand Up @@ -91,7 +92,7 @@ namespace centralized_configuration
for (const auto& groupId : groupIds)
{
const std::filesystem::path tmpGroupFile =
std::filesystem::temp_directory_path() / (groupId + config::DEFAULT_SHARED_FILE_EXTENSION);
m_fileSystemWrapper->temp_directory_path() / (groupId + config::DEFAULT_SHARED_FILE_EXTENSION);
m_downloadGroupFilesFunction(groupId, tmpGroupFile.string());
if (!m_validateFileFunction(tmpGroupFile))
{
Expand All @@ -101,18 +102,14 @@ namespace centralized_configuration

try
{
if (std::filesystem::exists(tmpGroupFile) &&
tmpGroupFile.parent_path() == std::filesystem::temp_directory_path())
if (m_fileSystemWrapper->exists(tmpGroupFile) &&
tmpGroupFile.parent_path() == m_fileSystemWrapper->temp_directory_path())
{
if (!std::filesystem::remove(tmpGroupFile))
if (!m_fileSystemWrapper->remove(tmpGroupFile))
{
LogWarn("Failed to delete invalid group file: {}", tmpGroupFile.string());
}
}
else
{
LogWarn("Skipping deletion of suspicious file path: {}", tmpGroupFile.string());
}
}
catch (const std::filesystem::filesystem_error& e)
{
Expand All @@ -131,8 +128,8 @@ namespace centralized_configuration

try
{
std::filesystem::create_directories(destGroupFile.parent_path());
std::filesystem::rename(tmpGroupFile, destGroupFile);
m_fileSystemWrapper->create_directories(destGroupFile.parent_path());
m_fileSystemWrapper->rename(tmpGroupFile, destGroupFile);
}
catch (const std::filesystem::filesystem_error& e)
{
Expand Down
2 changes: 1 addition & 1 deletion src/agent/centralized_configuration/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ find_package(GTest CONFIG REQUIRED)

add_executable(centralized_configuration_test centralized_configuration_tests.cpp)
configure_target(centralized_configuration_test)
target_link_libraries(centralized_configuration_test PRIVATE CentralizedConfiguration GTest::gtest GTest::gmock)
target_link_libraries(centralized_configuration_test PRIVATE CentralizedConfiguration GTest::gtest GTest::gmock GTest::gmock_main)
add_test(NAME CentralizedConfiguration COMMAND centralized_configuration_test)
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <centralized_configuration.hpp>
Expand All @@ -13,23 +14,41 @@
#include <string>
#include <vector>

#include <filesystem>
#include <ifilesystem.hpp>

using centralized_configuration::CentralizedConfiguration;
using namespace testing;

namespace
{
// NOLINTBEGIN(cppcoreguidelines-avoid-reference-coroutine-parameters)
boost::asio::awaitable<void> TestExecuteCommand(CentralizedConfiguration& centralizedConfiguration,
const std::string& command,
const nlohmann::json& parameters,
module_command::Status expectedErrorCode)
module_command::Status expectedErrorCode,
const std::string& expectedMessage)
{
const auto commandResult = co_await centralizedConfiguration.ExecuteCommand(command, parameters);
EXPECT_EQ(commandResult.ErrorCode, expectedErrorCode);
EXPECT_EQ(commandResult.Message, expectedMessage);
}

// NOLINTEND(cppcoreguidelines-avoid-reference-coroutine-parameters)
} // namespace

class MockFileSystem : public IFileSystem
{
public:
MOCK_METHOD(bool, exists, (const std::filesystem::path& path), (const, override));
MOCK_METHOD(bool, is_directory, (const std::filesystem::path& path), (const, override));
MOCK_METHOD(std::uintmax_t, remove_all, (const std::filesystem::path& path), (override));
MOCK_METHOD(std::filesystem::path, temp_directory_path, (), (const, override));
MOCK_METHOD(bool, create_directories, (const std::filesystem::path& path), (override));
MOCK_METHOD(void, rename, (const std::filesystem::path& from, const std::filesystem::path& to), (override));
MOCK_METHOD(bool, remove, (const std::filesystem::path& path), (override));
};

TEST(CentralizedConfiguration, Constructor)
{
EXPECT_NO_THROW([[maybe_unused]] CentralizedConfiguration centralizedConfiguration);
Expand All @@ -44,8 +63,11 @@ TEST(CentralizedConfiguration, ExecuteCommandReturnsFailureOnUnrecognizedCommand
[]() -> boost::asio::awaitable<void>
{
CentralizedConfiguration centralizedConfiguration;
co_await TestExecuteCommand(
centralizedConfiguration, "unknown-command", {}, module_command::Status::FAILURE);
co_await TestExecuteCommand(centralizedConfiguration,
"unknown-command",
{},
module_command::Status::FAILURE,
"CentralizedConfiguration command not recognized");
}(),
boost::asio::detached);

Expand All @@ -61,7 +83,16 @@ TEST(CentralizedConfiguration, ExecuteCommandReturnsFailureOnEmptyList)
[]() -> boost::asio::awaitable<void>
{
CentralizedConfiguration centralizedConfiguration;
co_await TestExecuteCommand(centralizedConfiguration, "set-group", {}, module_command::Status::FAILURE);
centralizedConfiguration.SetGroupIdFunction([](const std::vector<std::string>&) { return true; });
centralizedConfiguration.SetDownloadGroupFilesFunction([](const std::string&, const std::string&)
{ return true; });
centralizedConfiguration.ValidateFileFunction([](const std::filesystem::path&) { return true; });
centralizedConfiguration.ReloadModulesFunction([]() {});
co_await TestExecuteCommand(centralizedConfiguration,
"set-group",
{},
module_command::Status::FAILURE,
"CentralizedConfiguration group set failed, no group list");
}(),
boost::asio::detached);

Expand All @@ -77,10 +108,18 @@ TEST(CentralizedConfiguration, ExecuteCommandReturnsFailureOnParseParameters)
[]() -> boost::asio::awaitable<void>
{
CentralizedConfiguration centralizedConfiguration;
centralizedConfiguration.SetGroupIdFunction([](const std::vector<std::string>&) { return true; });
centralizedConfiguration.SetDownloadGroupFilesFunction([](const std::string&, const std::string&)
{ return true; });
centralizedConfiguration.ValidateFileFunction([](const std::filesystem::path&) { return true; });
centralizedConfiguration.ReloadModulesFunction([]() {});

const std::vector<std::string> parameterList = {true, "group2"};
co_await TestExecuteCommand(
centralizedConfiguration, "set-group", parameterList, module_command::Status::FAILURE);
co_await TestExecuteCommand(centralizedConfiguration,
"set-group",
parameterList,
module_command::Status::FAILURE,
"CentralizedConfiguration error while parsing parameters");
}(),
boost::asio::detached);

Expand All @@ -95,21 +134,44 @@ TEST(CentralizedConfiguration, ExecuteCommandHandlesRecognizedCommands)
io_context,
[]() -> boost::asio::awaitable<void>
{
CentralizedConfiguration centralizedConfiguration;
auto mockFileSystem = std::make_shared<MockFileSystem>();

EXPECT_CALL(*mockFileSystem, exists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*mockFileSystem, is_directory(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*mockFileSystem, remove_all(_)).WillRepeatedly(Return(0));
EXPECT_CALL(*mockFileSystem, temp_directory_path())
.WillRepeatedly(Return(std::filesystem::temp_directory_path()));
EXPECT_CALL(*mockFileSystem, create_directories(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*mockFileSystem, remove(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*mockFileSystem, rename(_, _)).WillRepeatedly(Return());

CentralizedConfiguration centralizedConfiguration(std::move(mockFileSystem));
centralizedConfiguration.SetGroupIdFunction([](const std::vector<std::string>&) { return true; });
centralizedConfiguration.GetGroupIdFunction([]() { return std::vector<std::string> {"group1", "group2"}; });
centralizedConfiguration.SetDownloadGroupFilesFunction([](const std::string&, const std::string&)
{ return true; });
centralizedConfiguration.ValidateFileFunction([](const std::filesystem::path&) { return true; });
centralizedConfiguration.ReloadModulesFunction([]() {});

const nlohmann::json groupsList = nlohmann::json::parse(R"([["group1", "group2"]])");

co_await TestExecuteCommand(
centralizedConfiguration, "set-group", groupsList, module_command::Status::SUCCESS);

co_await TestExecuteCommand(centralizedConfiguration, "update-group", {}, module_command::Status::SUCCESS);

co_await TestExecuteCommand(
centralizedConfiguration, "unknown-command", {}, module_command::Status::FAILURE);
co_await TestExecuteCommand(centralizedConfiguration,
"set-group",
groupsList,
module_command::Status::SUCCESS,
"CentralizedConfiguration 'set-group' done.");

co_await TestExecuteCommand(centralizedConfiguration,
"update-group",
{},
module_command::Status::SUCCESS,
"CentralizedConfiguration 'update-group' done.");

co_await TestExecuteCommand(centralizedConfiguration,
"unknown-command",
{},
module_command::Status::FAILURE,
"CentralizedConfiguration command not recognized");
}(),
boost::asio::detached);

Expand All @@ -124,12 +186,20 @@ TEST(CentralizedConfiguration, SetFunctionsAreCalledAndReturnsCorrectResultsForS
io_context,
[]() -> boost::asio::awaitable<void>
{
CentralizedConfiguration centralizedConfiguration;
auto mockFileSystem = std::make_shared<MockFileSystem>();

const nlohmann::json groupsList = nlohmann::json::parse(R"([["group1", "group2"]])");
EXPECT_CALL(*mockFileSystem, exists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*mockFileSystem, is_directory(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*mockFileSystem, remove_all(_)).WillRepeatedly(Return(0));
EXPECT_CALL(*mockFileSystem, temp_directory_path())
.WillRepeatedly(Return(std::filesystem::temp_directory_path()));
EXPECT_CALL(*mockFileSystem, create_directories(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*mockFileSystem, remove(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*mockFileSystem, rename(_, _)).WillRepeatedly(Return());

CentralizedConfiguration centralizedConfiguration(std::move(mockFileSystem));

co_await TestExecuteCommand(
centralizedConfiguration, "set-group", groupsList, module_command::Status::FAILURE);
const nlohmann::json groupsList = nlohmann::json::parse(R"([["group1", "group2"]])");

bool wasSetGroupIdFunctionCalled = false;
bool wasDownloadGroupFilesFunctionCalled = false;
Expand All @@ -148,11 +218,17 @@ TEST(CentralizedConfiguration, SetFunctionsAreCalledAndReturnsCorrectResultsForS
return wasDownloadGroupFilesFunctionCalled;
});

centralizedConfiguration.ValidateFileFunction([](const std::filesystem::path&) { return true; });
centralizedConfiguration.ReloadModulesFunction([]() {});

EXPECT_FALSE(wasSetGroupIdFunctionCalled);
EXPECT_FALSE(wasDownloadGroupFilesFunctionCalled);

co_await TestExecuteCommand(
centralizedConfiguration, "set-group", groupsList, module_command::Status::SUCCESS);
co_await TestExecuteCommand(centralizedConfiguration,
"set-group",
groupsList,
module_command::Status::SUCCESS,
"CentralizedConfiguration 'set-group' done.");

EXPECT_TRUE(wasSetGroupIdFunctionCalled);
EXPECT_TRUE(wasDownloadGroupFilesFunctionCalled);
Expand All @@ -170,9 +246,18 @@ TEST(CentralizedConfiguration, SetFunctionsAreCalledAndReturnsCorrectResultsForU
io_context,
[]() -> boost::asio::awaitable<void>
{
CentralizedConfiguration centralizedConfiguration;
auto mockFileSystem = std::make_shared<MockFileSystem>();

co_await TestExecuteCommand(centralizedConfiguration, "update-group", {}, module_command::Status::FAILURE);
EXPECT_CALL(*mockFileSystem, exists(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*mockFileSystem, is_directory(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*mockFileSystem, remove_all(_)).WillRepeatedly(Return(0));
EXPECT_CALL(*mockFileSystem, temp_directory_path())
.WillRepeatedly(Return(std::filesystem::temp_directory_path()));
EXPECT_CALL(*mockFileSystem, create_directories(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*mockFileSystem, remove(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*mockFileSystem, rename(_, _)).WillRepeatedly(Return());

CentralizedConfiguration centralizedConfiguration(std::move(mockFileSystem));

bool wasGetGroupIdFunctionCalled = false;
bool wasDownloadGroupFilesFunctionCalled = false;
Expand All @@ -191,10 +276,17 @@ TEST(CentralizedConfiguration, SetFunctionsAreCalledAndReturnsCorrectResultsForU
return wasDownloadGroupFilesFunctionCalled;
});

centralizedConfiguration.ValidateFileFunction([](const std::filesystem::path&) { return true; });
centralizedConfiguration.ReloadModulesFunction([]() {});

EXPECT_FALSE(wasGetGroupIdFunctionCalled);
EXPECT_FALSE(wasDownloadGroupFilesFunctionCalled);

co_await TestExecuteCommand(centralizedConfiguration, "update-group", {}, module_command::Status::SUCCESS);
co_await TestExecuteCommand(centralizedConfiguration,
"update-group",
{},
module_command::Status::SUCCESS,
"CentralizedConfiguration 'update-group' done.");

EXPECT_TRUE(wasGetGroupIdFunctionCalled);
EXPECT_TRUE(wasDownloadGroupFilesFunctionCalled);
Expand Down
1 change: 1 addition & 0 deletions src/common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ add_subdirectory(data_provider)
add_subdirectory(dbsync)
add_subdirectory(error_messages)
add_subdirectory(file_op)
add_subdirectory(filesystem_wrapper)
add_subdirectory(hashHelper)
add_subdirectory(logger)
add_subdirectory(mem_op)
Expand Down
15 changes: 15 additions & 0 deletions src/common/filesystem_wrapper/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
cmake_minimum_required(VERSION 3.22)

set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/../../vcpkg/scripts/buildsystems/vcpkg.cmake")
set(VCPKG_MANIFEST_DIR ${CMAKE_SOURCE_DIR}/../../)

project(FilesystemWrapper)

include(../../cmake/CommonSettings.cmake)
set_common_settings()

add_library(FilesystemWrapper STATIC
src/filesystem_wrapper.cpp
)

target_include_directories(FilesystemWrapper PUBLIC include)
Loading

0 comments on commit c824964

Please sign in to comment.