/** * @file test_init.cpp * @brief Implementation of component initialization helpers for unit tests. * * Each test_init_*() function creates real instances of ProxySQL components, * bypassing the full daemon startup sequence. Components are assigned to * their respective Glo* global pointers so that internal cross-references * work correctly. * * @see test_init.h for the public interface and usage examples. * @see Phase 2.1 of the Unit Testing Framework (GitHub issue #5473) */ #include "proxysql.h" #include "cpp.h" #include "MySQL_Authentication.hpp" #include "PgSQL_Authentication.h" #include "MySQL_Query_Cache.h" #include "PgSQL_Query_Cache.h" #include "MySQL_Query_Processor.h" #include "PgSQL_Query_Processor.h" #include "test_globals.h" #include "test_init.h" // Extern declarations for Glo* pointers defined in test_globals.cpp. // These are normally defined in main.cpp and have no header declarations. extern MySQL_Authentication *GloMyAuth; extern PgSQL_Authentication *GloPgAuth; extern MySQL_Query_Cache *GloMyQC; extern PgSQL_Query_Cache *GloPgQC; extern MySQL_Query_Processor *GloMyQPro; extern PgSQL_Query_Processor *GloPgQPro; // GloMTH is declared extern in proxysql_utils.h. // GloPTH has no extern declaration in any header, so we add one here. extern PgSQL_Threads_Handler *GloPTH; // ============================================================================ // Minimal initialization // ============================================================================ int test_init_minimal() { return test_globals_init(); } void test_cleanup_minimal() { test_globals_cleanup(); } // ============================================================================ // Authentication // ============================================================================ int test_init_auth() { if (GloMyAuth != nullptr || GloPgAuth != nullptr) { // Already initialized — idempotent return 0; } GloMyAuth = new MySQL_Authentication(); GloPgAuth = new PgSQL_Authentication(); return 0; } void test_cleanup_auth() { if (GloMyAuth != nullptr) { delete GloMyAuth; GloMyAuth = nullptr; } if (GloPgAuth != nullptr) { delete GloPgAuth; GloPgAuth = nullptr; } } // ============================================================================ // Query Cache // ============================================================================ int test_init_query_cache() { if (GloMyQC != nullptr || GloPgQC != nullptr) { return 0; } // The Query_Cache constructor registers Prometheus metrics via // GloVars.prometheus_registry. Provide a real registry so the // constructor doesn't crash on nullptr dereference. if (GloVars.prometheus_registry == nullptr) { GloVars.prometheus_registry = std::make_shared(); } GloMyQC = new MySQL_Query_Cache(); GloPgQC = new PgSQL_Query_Cache(); // NOTE: We intentionally do NOT start the purge thread here. // Unit tests should call purgeHash() explicitly for deterministic // behavior. return 0; } void test_cleanup_query_cache() { if (GloMyQC != nullptr) { delete GloMyQC; GloMyQC = nullptr; } if (GloPgQC != nullptr) { delete GloPgQC; GloPgQC = nullptr; } } // ============================================================================ // Query Processor // ============================================================================ int test_init_query_processor() { if (GloMyQPro != nullptr || GloPgQPro != nullptr) { return 0; } // Query Processor constructors register Prometheus metrics and // read variables from GloMTH/GloPTH. Ensure both are available. if (GloVars.prometheus_registry == nullptr) { GloVars.prometheus_registry = std::make_shared(); } if (GloMTH == nullptr) { GloMTH = new MySQL_Threads_Handler(); } if (GloPTH == nullptr) { GloPTH = new PgSQL_Threads_Handler(); } // Trigger lazy initialization of VariablesPointers maps. // The QP constructor calls get_variable_int() which requires // these maps to be populated. char **vl = GloMTH->get_variables_list(); if (vl) { for (char **p = vl; *p != nullptr; ++p) free(*p); free(vl); } vl = GloPTH->get_variables_list(); if (vl) { for (char **p = vl; *p != nullptr; ++p) free(*p); free(vl); } GloMyQPro = new MySQL_Query_Processor(); GloPgQPro = new PgSQL_Query_Processor(); return 0; } void test_cleanup_query_processor() { if (GloMyQPro != nullptr) { delete GloMyQPro; GloMyQPro = nullptr; } if (GloPgQPro != nullptr) { delete GloPgQPro; GloPgQPro = nullptr; } // NOTE: We do NOT delete GloMTH/GloPTH here because other // components may still reference them. Their cleanup relies // on process exit. } // ============================================================================ // HostGroups Manager // ============================================================================ int test_init_hostgroups() { // HostGroups Manager constructors register Prometheus metrics. if (GloVars.prometheus_registry == nullptr) { GloVars.prometheus_registry = std::make_shared(); } if (MyHGM == nullptr) { MyHGM = new MySQL_HostGroups_Manager(); // NOTE: We intentionally do NOT call MyHGM->init() here. // init() starts background threads (HGCU_thread, GTID_syncer) // that run forever and would cause the test process to hang on // exit. The constructor alone sets up the internal SQLite3 // database and all data structures needed for unit testing. } if (PgHGM == nullptr) { PgHGM = new PgSQL_HostGroups_Manager(); // PgHGM->init() is a no-op, but we skip it for consistency. } return 0; } void test_cleanup_hostgroups() { if (MyHGM != nullptr) { delete MyHGM; MyHGM = nullptr; } if (PgHGM != nullptr) { delete PgHGM; PgHGM = nullptr; } }