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);
+}