From fe8dc6ceba3ed1192afb2e4e70ae51c38aab4f73 Mon Sep 17 00:00:00 2001 From: Wessel Tip Date: Thu, 2 Oct 2025 13:17:41 +0200 Subject: [PATCH] feat(Config,Database): Add tests --- .../CMakeLists.txt | 48 +++++-- src/g2_2025_grade_calculator_pkg/package.xml | 3 - .../test/ConfigManager.test.cpp | 135 ++++++++++++++++++ .../test/DatabaseManager.test.cpp | 79 ++++++++++ 4 files changed, 250 insertions(+), 15 deletions(-) create mode 100644 src/g2_2025_grade_calculator_pkg/test/ConfigManager.test.cpp create mode 100644 src/g2_2025_grade_calculator_pkg/test/DatabaseManager.test.cpp diff --git a/src/g2_2025_grade_calculator_pkg/CMakeLists.txt b/src/g2_2025_grade_calculator_pkg/CMakeLists.txt index 796be24..bb0c855 100644 --- a/src/g2_2025_grade_calculator_pkg/CMakeLists.txt +++ b/src/g2_2025_grade_calculator_pkg/CMakeLists.txt @@ -8,13 +8,13 @@ endif() # external packages include(FetchContent) -FetchContent_Declare( +fetchcontent_declare( tomlplusplus GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git GIT_TAG v3.4.0 ) -FetchContent_MakeAvailable(tomlplusplus) +fetchcontent_makeavailable(tomlplusplus) # find dependencies find_package(ament_cmake REQUIRED) @@ -64,7 +64,7 @@ add_executable(retake_scheduler ) ament_target_dependencies(retake_scheduler rclcpp g2_2025_interfaces) -install ( +install( TARGETS exam_result_generator final_grade_determinator @@ -75,15 +75,39 @@ install ( ) if(BUILD_TESTING) - find_package(ament_lint_auto REQUIRED) - # the following line skips the linter which checks for copyrights - # comment the line when a copyright and license is added to all source files - set(ament_cmake_copyright_FOUND TRUE) - # the following line skips cpplint (only works in a git repo) - # comment the line when this package is in a git repo and when - # a copyright and license is added to all source files - set(ament_cmake_cpplint_FOUND TRUE) - ament_lint_auto_find_test_dependencies() + find_package(ament_cmake_gtest REQUIRED) + + # Add gtest for ConfigManager + ament_add_gtest(${PROJECT_NAME}_test_config_manager + test/ConfigManager.test.cpp + src/config/ConfigManager.cpp + ) + target_include_directories(${PROJECT_NAME}_test_config_manager PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + ament_target_dependencies(${PROJECT_NAME}_test_config_manager + rclcpp + ) + target_link_libraries(${PROJECT_NAME}_test_config_manager + tomlplusplus::tomlplusplus + ) + + # Add gtest for DatabaseManager + ament_add_gtest(${PROJECT_NAME}_test_database_manager + test/DatabaseManager.test.cpp + src/database/DatabaseManager.cpp + src/config/ConfigManager.cpp + ) + target_include_directories(${PROJECT_NAME}_test_database_manager PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src + ) + ament_target_dependencies(${PROJECT_NAME}_test_database_manager + rclcpp + g2_2025_interfaces + ) + target_link_libraries(${PROJECT_NAME}_test_database_manager + pqxx pq tomlplusplus::tomlplusplus + ) endif() ament_package() diff --git a/src/g2_2025_grade_calculator_pkg/package.xml b/src/g2_2025_grade_calculator_pkg/package.xml index 31cb72c..fb3cd5e 100644 --- a/src/g2_2025_grade_calculator_pkg/package.xml +++ b/src/g2_2025_grade_calculator_pkg/package.xml @@ -12,9 +12,6 @@ rclcpp g2_2025_interfaces - ament_lint_auto - ament_lint_common - ament_cmake diff --git a/src/g2_2025_grade_calculator_pkg/test/ConfigManager.test.cpp b/src/g2_2025_grade_calculator_pkg/test/ConfigManager.test.cpp new file mode 100644 index 0000000..69b0068 --- /dev/null +++ b/src/g2_2025_grade_calculator_pkg/test/ConfigManager.test.cpp @@ -0,0 +1,135 @@ +#include +#include +#include +#include + +#include "config/ConfigManager.hpp" + +using namespace assignments::one; + +static const std::string TEST_CONFIG_CONTENT = R"( +[database] +host = "test_host" +port = 1234 +dbname = "test_db" +user = "test_user" +password = "test_password" +timeout = 60 +ssl = true + +[database.pool] +min_connections = 2 +max_connections = 20 +)"; + +static const std::string TEST_CONFIG_NO_POOL_CONTENT = R"( +[database] +host = "localhost" +port = 5432 +dbname = "grades" +user = "postgres" +password = "postgres" +)"; + + +class ConfigManagerTest : public ::testing::Test { +protected: + void SetUp() override { + rclcpp::init(0, nullptr); + node_ = std::make_shared("test_config_node"); + } + + void TearDown() override { + // Clean up temporary files + if (std::filesystem::exists(temp_config_file_)) { + std::filesystem::remove(temp_config_file_); + } + + node_.reset(); + rclcpp::shutdown(); + } + + void create_temp_config_file() { + temp_config_file_ = "test_config.toml"; + std::ofstream file(temp_config_file_); + file << TEST_CONFIG_CONTENT; + file.close(); + } + + std::shared_ptr node_; + std::string temp_config_file_; +}; + +TEST_F(ConfigManagerTest, ConstructorTest) { + // Test ConfigManager can be constructed with logger + ConfigManager config_manager(node_->get_logger()); + // Constructor should not crash + SUCCEED(); +} + +TEST_F(ConfigManagerTest, LoadValidConfigTest) { + create_temp_config_file(); + + ConfigManager config_manager(node_->get_logger()); + bool result = config_manager.load_config(temp_config_file_); + + EXPECT_TRUE(result); + EXPECT_TRUE(config_manager.is_loaded()); +} + +TEST_F(ConfigManagerTest, LoadInvalidFileTest) { + ConfigManager config_manager(node_->get_logger()); + bool result = config_manager.load_config("non_existent_file.toml"); + + EXPECT_FALSE(result); + EXPECT_FALSE(config_manager.is_loaded()); +} + +TEST_F(ConfigManagerTest, DatabaseConfigParsingTest) { + create_temp_config_file(); + + ConfigManager config_manager(node_->get_logger()); + config_manager.load_config(temp_config_file_); + + auto db_config = config_manager.get_database_config(); + + ASSERT_TRUE(db_config.has_value()); + EXPECT_EQ(db_config->host, "test_host"); + EXPECT_EQ(db_config->port, 1234); + EXPECT_EQ(db_config->dbname, "test_db"); + EXPECT_EQ(db_config->user, "test_user"); + EXPECT_EQ(db_config->password, "test_password"); + EXPECT_EQ(db_config->timeout, 60); + EXPECT_TRUE(db_config->ssl); + EXPECT_EQ(db_config->min_connections, 2); + EXPECT_EQ(db_config->max_connections, 20); +} + +TEST_F(ConfigManagerTest, DatabaseConfigWithoutPoolTest) { + // Create config without pool section + std::ofstream file("test_config_no_pool.toml"); + file << TEST_CONFIG_NO_POOL_CONTENT; + file.close(); + + ConfigManager config_manager(node_->get_logger()); + config_manager.load_config("test_config_no_pool.toml"); + + auto db_config = config_manager.get_database_config(); + + ASSERT_TRUE(db_config.has_value()); + EXPECT_EQ(db_config->host, "localhost"); + EXPECT_EQ(db_config->port, 5432); + EXPECT_EQ(db_config->min_connections, 1); // default + EXPECT_EQ(db_config->max_connections, 10); // default + + std::filesystem::remove("test_config_no_pool.toml"); +} + +TEST_F(ConfigManagerTest, GetConfigWithoutLoadingTest) { + ConfigManager config_manager(node_->get_logger()); + + auto db_config = config_manager.get_database_config(); + + EXPECT_FALSE(db_config.has_value()); + EXPECT_FALSE(config_manager.is_loaded()); +} diff --git a/src/g2_2025_grade_calculator_pkg/test/DatabaseManager.test.cpp b/src/g2_2025_grade_calculator_pkg/test/DatabaseManager.test.cpp new file mode 100644 index 0000000..417dd8e --- /dev/null +++ b/src/g2_2025_grade_calculator_pkg/test/DatabaseManager.test.cpp @@ -0,0 +1,79 @@ +#include +#include + +#include "database/DatabaseManager.hpp" +#include "database/StudentCourse.hpp" + +using namespace assignments::one; + +class DatabaseManagerTest : public ::testing::Test { +protected: + void SetUp() override { + rclcpp::init(0, nullptr); + + node_ = std::make_shared("test_db_node"); + db_manager_ = std::make_unique(node_->get_logger()); + } + + void TearDown() override { + db_manager_.reset(); + node_.reset(); + rclcpp::shutdown(); + } + + std::shared_ptr node_; + std::unique_ptr db_manager_; +}; + +TEST_F(DatabaseManagerTest, ConstructorTest) { + // Test DatabaseManager can be constructed + ASSERT_NE(db_manager_, nullptr); +} + +TEST_F(DatabaseManagerTest, ConnectionStatusTest) { + bool status = db_manager_->is_connected(); + + EXPECT_TRUE(status == true || status == false); +} + +TEST_F(DatabaseManagerTest, QueuePendingCombinationsTest) { + auto combinations = db_manager_->queue_pending_combinations(); + + EXPECT_GE(combinations.size(), 0); +} + +TEST_F(DatabaseManagerTest, StoreExamResultTest) { + bool result = db_manager_->store_exam_result("TestStudent", "TestCourse", 85); + + EXPECT_TRUE(result == true || result == false); +} + +TEST_F(DatabaseManagerTest, EnrollStudentTest) { + StudentCourse sc; + sc.student_name = "TestStudent"; + sc.course_name = "TestCourse"; + + bool result = db_manager_->enroll_student_into_course(sc); + + EXPECT_TRUE(result == true || result == false); +} + +TEST_F(DatabaseManagerTest, GetFinalGradeTest) { + StudentCourse sc; + sc.student_name = "NonExistentStudent"; + sc.course_name = "NonExistentCourse"; + + int grade = db_manager_->get_final_course_grade(sc); + + EXPECT_EQ(grade, -1); +} + +TEST_F(DatabaseManagerTest, StoreFinalResultTest) { + StudentCourse sc; + sc.student_name = "TestStudent"; + sc.course_name = "TestCourse"; + + bool result = db_manager_->store_final_course_result(sc, 3, 75); + + EXPECT_TRUE(result == true || result == false); +}