30#define TEST_START(name) \
31 std::cout << "\n[TEST] " << name << "...\n"
33#define ASSERT_TRUE(condition, message) \
36 std::cout << " ā FAILED: " << message << "\n"; \
37 std::cout << " at " << __FILE__ << ":" << __LINE__ << "\n"; \
43#define ASSERT_FALSE(condition, message) \
44 ASSERT_TRUE(!(condition), message)
48 std::cout << " ā
PASSED\n"; \
67class stub_backend :
public ::database::core::database_backend {
69 stub_backend() =
default;
71 static std::unique_ptr<::database::core::database_backend> create() {
72 return std::make_unique<stub_backend>();
75 ::database::database_types type()
const override {
76 return ::database::database_types::sqlite;
79 kcenon::common::VoidResult initialize(
80 const ::database::core::connection_config& )
override {
82 return kcenon::common::VoidResult(std::monostate{});
85 kcenon::common::VoidResult shutdown()
override {
88 return kcenon::common::VoidResult(std::monostate{});
91 bool is_initialized()
const override {
return initialized_; }
93 kcenon::common::Result<::database::core::database_result> select_query(
94 const std::string& )
override {
95 ::database::core::database_result result;
96 ::database::core::database_row row;
97 row[
"id"] = int64_t{1};
98 row[
"name"] = std::string(
"test_user");
99 result.push_back(row);
103 kcenon::common::VoidResult execute_query(
104 const std::string& )
override {
105 return kcenon::common::VoidResult(std::monostate{});
108 kcenon::common::VoidResult begin_transaction()
override {
110 return kcenon::common::VoidResult(std::monostate{});
113 kcenon::common::VoidResult commit_transaction()
override {
115 return kcenon::common::VoidResult(std::monostate{});
118 kcenon::common::VoidResult rollback_transaction()
override {
120 return kcenon::common::VoidResult(std::monostate{});
123 bool in_transaction()
const override {
return in_tx_; }
125 std::string last_error()
const override {
return ""; }
127 std::map<std::string, std::string> connection_info()
const override {
128 return {{
"backend",
"stub"}, {
"version",
"1.0"}};
132 bool initialized_ =
false;
137void register_stub_backend() {
138 auto& registry = ::database::core::backend_registry::instance();
139 if (!registry.has_backend(
"sqlite")) {
140 registry.register_backend(
"sqlite", &stub_backend::create);
144void unregister_stub_backend() {
145 ::database::core::backend_registry::instance().unregister_backend(
"sqlite");
155 TEST_START(
"Builder Pattern - Default Configuration");
158 auto db_result = builder.build();
160 ASSERT_TRUE(db_result.is_ok(),
"Builder should succeed");
161 auto db = std::move(db_result.value());
162 ASSERT_TRUE(db !=
nullptr,
"Builder should create database instance");
172 TEST_START(
"Builder Pattern - Custom Configuration");
184 if (db_result.is_err()) {
187 std::cout <<
" Note: Database connection not available: " << db_result.error().message <<
"\n";
188 std::cout <<
" Builder API test passed (connection test skipped)\n";
190 ASSERT_TRUE(db_result.value() !=
nullptr,
"Builder with custom config should create instance");
217 TEST_START(
"Configuration-Based Construction");
244 ASSERT_TRUE(db1_result.is_ok(),
"Builder should succeed");
245 auto db1 = std::move(db1_result.value());
246 ASSERT_TRUE(db1 !=
nullptr,
"Original instance should be valid");
249 auto db2 = std::move(db1);
250 ASSERT_TRUE(db2 !=
nullptr,
"Moved instance should be valid");
254 ASSERT_TRUE(db3_result.is_ok(),
"Builder should succeed");
255 auto db3 = std::move(db3_result.value());
256 db3 = std::move(db2);
257 ASSERT_TRUE(db3 !=
nullptr,
"Move-assigned instance should be valid");
267 TEST_START(
"Connection State Management API");
300 health.status == health_status::failed ||
301 health.status == health_status::critical,
302 "Health check should show non-healthy status when disconnected"
305 ASSERT_FALSE(health.is_connected,
"Health check should show not connected");
323 ASSERT_TRUE(metrics.total_queries == 0,
"Initial query count should be 0");
324 ASSERT_TRUE(metrics.successful_queries == 0,
"Initial success count should be 0");
325 ASSERT_TRUE(metrics.failed_queries == 0,
"Initial failure count should be 0");
326 ASSERT_TRUE(metrics.active_connections == 0,
"Initial connections should be 0");
346 result.
rows.push_back({{
"id",
"1"}, {
"name",
"test"}});
354 auto& row = result[0];
355 ASSERT_TRUE(row.at(
"id") ==
"1",
"Row data should be accessible");
356 ASSERT_TRUE(row.at(
"name") ==
"test",
"Row data should be correct");
360 for (
const auto& r : result) {
364 ASSERT_TRUE(count == 1,
"Should iterate over all rows");
377 std::vector<query_param> params;
385 ASSERT_TRUE(params.size() == 5,
"Should accept various parameter types");
386 ASSERT_TRUE(params[0].get_value() ==
"string value",
"String param should work");
387 ASSERT_TRUE(params[1].get_value() ==
"42",
"Int param should convert to string");
388 ASSERT_TRUE(params[3].get_value() ==
"true",
"Bool true should convert correctly");
389 ASSERT_TRUE(params[4].get_value() ==
"false",
"Bool false should convert correctly");
392 ASSERT_FALSE(params[0].is_null(),
"String param should not be null");
393 ASSERT_FALSE(params[1].is_null(),
"Int param should not be null");
412 const char* null_str =
nullptr;
417 const char* valid_str =
"test";
429 std::string str =
"moved value";
435 query_param ll_param(
static_cast<long long>(123456789012345LL));
438 query_param ull_param(
static_cast<unsigned long long>(18446744073709551615ULL));
486 ASSERT_TRUE(health.
status == health_status::healthy,
"Default status is healthy");
491 health.
status = health_status::degraded;
497 health.
issues.push_back(
"Test issue");
512 TEST_START(
"Thread Safety - Concurrent Health Checks");
517 std::vector<std::thread> threads;
518 std::atomic<size_t> checks_completed{0};
520 for (
int i = 0; i < 10; ++i) {
521 threads.emplace_back([&db, &checks_completed]() {
522 for (
int j = 0; j < 100; ++j) {
531 for (
auto& t : threads) {
535 ASSERT_TRUE(checks_completed == 10,
"All threads should complete");
545 TEST_START(
"Thread Safety - Concurrent Metrics Retrieval");
549 std::vector<std::thread> threads;
550 std::atomic<size_t> retrievals_completed{0};
552 for (
int i = 0; i < 10; ++i) {
553 threads.emplace_back([&db, &retrievals_completed]() {
554 for (
int j = 0; j < 100; ++j) {
558 retrievals_completed++;
562 for (
auto& t : threads) {
566 ASSERT_TRUE(retrievals_completed == 10,
"All threads should complete");
576 TEST_START(
"Error Handling - Query Without Connection");
581 auto result = db.
execute(
"SELECT 1");
583#if defined(USE_COMMON_SYSTEM)
584 ASSERT_TRUE(result.is_err(),
"Query without connection should fail");
586 ASSERT_TRUE(result.is_error(),
"Query without connection should fail");
597 TEST_START(
"Connect with Mock Backend (SQLite stub)");
599 register_stub_backend();
602 auto result = db.
connect(backend_type::sqlite,
":memory:");
603 ASSERT_TRUE(result.is_ok(),
"Connect with stub backend should succeed");
607 ASSERT_TRUE(disconnect_result.is_ok(),
"Disconnect should succeed");
620 register_stub_backend();
623 auto result = db.
connect(backend_type::sqlite,
":memory:");
624 ASSERT_TRUE(result.is_ok(),
"Typed connect with stub should succeed");
638 auto result = db.
connect(backend_type::mongodb,
"localhost:27017");
640#if defined(USE_COMMON_SYSTEM)
641 ASSERT_TRUE(result.is_err(),
"Connect with unsupported backend should fail");
643 ASSERT_TRUE(result.is_error(),
"Connect with unsupported backend should fail");
658 register_stub_backend();
661 auto conn = db.
connect(backend_type::sqlite,
":memory:");
662 ASSERT_TRUE(conn.is_ok(),
"Connect should succeed");
664 auto result = db.
execute(
"SELECT * FROM users WHERE id = 1");
665 ASSERT_TRUE(result.is_ok(),
"Execute on connected db should succeed");
667 auto& qr = result.value();
668 ASSERT_TRUE(qr.size() == 1,
"Should return one row from stub");
669 ASSERT_TRUE(qr[0].at(
"name") ==
"test_user",
"Row should contain stub data");
681 register_stub_backend();
684 db.
connect(backend_type::sqlite,
":memory:");
686 auto result = db.
select(
"SELECT id, name FROM users");
687 ASSERT_TRUE(result.is_ok(),
"Select should succeed");
689 auto& qr = result.value();
691 ASSERT_TRUE(qr[0].count(
"id") > 0,
"Row should have 'id' column");
701 TEST_START(
"Select Failure Path - Not Connected");
704 auto result = db.
select(
"SELECT 1");
706#if defined(USE_COMMON_SYSTEM)
707 ASSERT_TRUE(result.is_err(),
"Select without connection should fail");
709 ASSERT_TRUE(result.is_error(),
"Select without connection should fail");
722 register_stub_backend();
725 db.
connect(backend_type::sqlite,
":memory:");
727 auto result = db.
insert(
"INSERT INTO users (name) VALUES ('Alice')");
728 ASSERT_TRUE(result.is_ok(),
"Insert should succeed");
730 auto affected = result.value();
731 ASSERT_TRUE(affected > 0,
"Insert should affect at least one row");
741 TEST_START(
"Insert Failure Path - Not Connected");
744 auto result = db.
insert(
"INSERT INTO users (name) VALUES ('Bob')");
746#if defined(USE_COMMON_SYSTEM)
747 ASSERT_TRUE(result.is_err(),
"Insert without connection should fail");
749 ASSERT_TRUE(result.is_error(),
"Insert without connection should fail");
762 register_stub_backend();
765 db.
connect(backend_type::sqlite,
":memory:");
767 auto result = db.
update(
"UPDATE users SET name = 'Bob' WHERE id = 1");
768 ASSERT_TRUE(result.is_ok(),
"Update should succeed");
770 auto affected = result.value();
771 ASSERT_TRUE(affected > 0,
"Update should affect at least one row");
781 TEST_START(
"Update Failure Path - Not Connected");
784 auto result = db.
update(
"UPDATE users SET name = 'Bob'");
786#if defined(USE_COMMON_SYSTEM)
787 ASSERT_TRUE(result.is_err(),
"Update without connection should fail");
789 ASSERT_TRUE(result.is_error(),
"Update without connection should fail");
802 register_stub_backend();
805 db.
connect(backend_type::sqlite,
":memory:");
807 auto result = db.
remove(
"DELETE FROM users WHERE id = 1");
808 ASSERT_TRUE(result.is_ok(),
"Remove should succeed");
810 auto affected = result.value();
811 ASSERT_TRUE(affected > 0,
"Remove should affect at least one row");
821 TEST_START(
"Remove Failure Path - Not Connected");
824 auto result = db.
remove(
"DELETE FROM users WHERE id = 1");
826#if defined(USE_COMMON_SYSTEM)
827 ASSERT_TRUE(result.is_err(),
"Remove without connection should fail");
829 ASSERT_TRUE(result.is_error(),
"Remove without connection should fail");
842 register_stub_backend();
845 db.
connect(backend_type::sqlite,
":memory:");
848 ASSERT_TRUE(tx_result.is_ok(),
"Begin transaction should succeed when connected");
850 auto& tx = tx_result.value();
851 ASSERT_TRUE(tx !=
nullptr,
"Transaction pointer should be valid");
852 ASSERT_TRUE(tx->is_active(),
"Transaction should be active after begin");
862 TEST_START(
"Begin Transaction Failure Path - Not Connected");
867#if defined(USE_COMMON_SYSTEM)
868 ASSERT_TRUE(tx_result.is_err(),
"Begin transaction without connection should fail");
870 ASSERT_TRUE(tx_result.is_error(),
"Begin transaction without connection should fail");
883 register_stub_backend();
886 db.
connect(backend_type::sqlite,
":memory:");
889 ASSERT_TRUE(tx_result.is_ok(),
"Begin transaction should succeed");
891 auto& tx = tx_result.value();
892 auto exec_result = tx->execute(
"INSERT INTO users (name) VALUES ('Alice')");
893 ASSERT_TRUE(exec_result.is_ok(),
"Transaction execute should succeed");
905 register_stub_backend();
908 db.
connect(backend_type::sqlite,
":memory:");
911 ASSERT_TRUE(tx_result.is_ok(),
"Begin transaction should succeed");
913 auto& tx = tx_result.value();
914 ASSERT_TRUE(tx->is_active(),
"Transaction should be active before commit");
916 auto commit_result = tx->commit();
917 ASSERT_TRUE(commit_result.is_ok(),
"Commit should succeed");
918 ASSERT_FALSE(tx->is_active(),
"Transaction should not be active after commit");
930 register_stub_backend();
933 db.
connect(backend_type::sqlite,
":memory:");
936 ASSERT_TRUE(tx_result.is_ok(),
"Begin transaction should succeed");
938 auto& tx = tx_result.value();
939 ASSERT_TRUE(tx->is_active(),
"Transaction should be active before rollback");
941 auto rollback_result = tx->rollback();
942 ASSERT_TRUE(rollback_result.is_ok(),
"Rollback should succeed");
943 ASSERT_FALSE(tx->is_active(),
"Transaction should not be active after rollback");
953 TEST_START(
"Transaction is_active State Tracking");
955 register_stub_backend();
958 db.
connect(backend_type::sqlite,
":memory:");
961 ASSERT_TRUE(tx_result.is_ok(),
"Begin transaction should succeed");
963 auto& tx = tx_result.value();
966 ASSERT_TRUE(tx->is_active(),
"Should be active after begin");
969 tx->execute(
"SELECT 1");
970 ASSERT_TRUE(tx->is_active(),
"Should remain active after execute");
974 ASSERT_FALSE(tx->is_active(),
"Should be inactive after commit");
977 auto double_commit = tx->commit();
978#if defined(USE_COMMON_SYSTEM)
979 ASSERT_TRUE(double_commit.is_err(),
"Double commit should fail");
981 ASSERT_TRUE(double_commit.is_error(),
"Double commit should fail");
994 register_stub_backend();
997 db.
connect(backend_type::sqlite,
":memory:");
1001 ASSERT_TRUE(tx_result.is_ok(),
"Begin transaction should succeed");
1003 auto& tx = tx_result.value();
1004 tx->execute(
"INSERT INTO users (name) VALUES ('Alice')");
1011 ASSERT_TRUE(
true,
"Transaction destructor should auto-rollback without crash");
1021 TEST_START(
"Execute Transaction - Batch Success");
1023 register_stub_backend();
1026 db.
connect(backend_type::sqlite,
":memory:");
1028 std::vector<std::string> queries = {
1029 "INSERT INTO users (name) VALUES ('Alice')",
1030 "INSERT INTO users (name) VALUES ('Bob')",
1031 "UPDATE users SET name = 'Charlie' WHERE id = 1"
1035 ASSERT_TRUE(result.is_ok(),
"Batch transaction should succeed");
1045 TEST_START(
"Execute Transaction - Not Connected");
1049 std::vector<std::string> queries = {
"INSERT INTO users (name) VALUES ('Alice')"};
1052#if defined(USE_COMMON_SYSTEM)
1053 ASSERT_TRUE(result.is_err(),
"Batch transaction without connection should fail");
1055 ASSERT_TRUE(result.is_error(),
"Batch transaction without connection should fail");
1076 ASSERT_TRUE(retrieved.database.type == backend_type::sqlite,
1077 "Config should reflect configured backend type");
1078 ASSERT_TRUE(retrieved.connection_pool.min_connections == 3,
1079 "Config should reflect configured min connections");
1080 ASSERT_TRUE(retrieved.connection_pool.max_connections == 15,
1081 "Config should reflect configured max connections");
1097 "Default backend type should be postgres");
1114 "Not connected: total connections should be 0");
1116 "Not connected: active connections should be 0");
1119 register_stub_backend();
1120 db.
connect(backend_type::sqlite,
":memory:");
1123 ASSERT_TRUE(connected_stats.total_connections == 1,
1124 "Connected: total connections should be 1");
1125 ASSERT_TRUE(connected_stats.active_connections == 1,
1126 "Connected: active connections should be 1");
1138 register_stub_backend();
1141 db.
connect(backend_type::sqlite,
":memory:");
1149 "Should have at least 2 queries recorded");
1156 "After reset, total queries should be 0");
1157 ASSERT_TRUE(metrics_after.successful_queries == 0,
1158 "After reset, successful queries should be 0");
1160 "After reset, failed queries should be 0");
1176 ASSERT_TRUE(
true,
"create_query_builder should return without error");
1186 TEST_START(
"Metrics Update on Successful Query");
1188 register_stub_backend();
1191 db.
connect(backend_type::sqlite,
":memory:");
1200 ASSERT_TRUE(metrics.total_queries == 3,
"Should record 3 queries");
1201 ASSERT_TRUE(metrics.successful_queries == 3,
"All 3 should be successful");
1202 ASSERT_TRUE(metrics.failed_queries == 0,
"No failures expected");
1214 register_stub_backend();
1217 db.
connect(backend_type::sqlite,
":memory:");
1223 ASSERT_TRUE(tx_result.is_ok(),
"Begin should succeed");
1224 auto& tx = tx_result.value();
1230 "Should record at least 1 transaction started");
1241 std::cout <<
"========================================\n";
1242 std::cout <<
"Phase 6: Unified Database System Tests\n";
1243 std::cout <<
"========================================\n";
1302 unregister_stub_backend();
1306 std::cout <<
"========================================\n";
1307 std::cout <<
"Test Summary\n";
1308 std::cout <<
"========================================\n";
1313 std::cout <<
"\nā
All tests passed!\n\n";
1316 std::cout <<
"\nā Some tests failed!\n\n";
Registry for database backend plugins.
builder & enable_monitoring(bool enable=true)
Enable monitoring and metrics collection.
builder & set_connection_string(const std::string &conn_str)
Set the connection string.
builder & set_pool_size(size_t min_size, size_t max_size)
Set connection pool size.
builder & enable_async(size_t worker_threads=4)
Enable async operations.
builder & set_slow_query_threshold(std::chrono::milliseconds threshold)
Set slow query threshold.
kcenon::common::Result< std::unique_ptr< unified_database_system > > build()
Build and return the configured database system.
builder & enable_logging(db_log_level level, const std::string &log_dir="./logs")
Enable logging.
builder & set_backend(backend_type type)
Set the database backend type.
Main unified database system class.
const unified_db_config & get_config() const
Get current configuration.
kcenon::common::Result< std::unique_ptr< transaction > > begin_transaction()
void reset_metrics()
Reset metrics counters.
backend_type get_backend_type() const
Get database backend type.
database_metrics get_metrics() const
Get current performance metrics.
kcenon::common::Result< size_t > update(const std::string &query, const std::vector< query_param > ¶ms={})
Execute an UPDATE query.
kcenon::common::Result< query_result > execute(const std::string &query, const std::vector< query_param > ¶ms={})
health_check check_health() const
Perform health check.
kcenon::common::VoidResult execute_transaction(const std::vector< std::string > &queries)
Execute multiple queries in a transaction.
kcenon::common::VoidResult connect(const std::string &connection_string)
Connect to database.
static builder create_builder()
Create a builder for custom configuration.
kcenon::common::Result< size_t > insert(const std::string &query, const std::vector< query_param > ¶ms={})
Execute an INSERT query.
kcenon::common::Result< query_result > select(const std::string &query, const std::vector< query_param > ¶ms={})
Execute a SELECT query.
kcenon::common::Result< size_t > remove(const std::string &query, const std::vector< query_param > ¶ms={})
Execute a DELETE query.
pool_stats get_pool_stats() const
kcenon::common::VoidResult disconnect()
Disconnect from database.
query_builder create_query_builder() const
bool is_connected() const
Check if connected to database.
Abstract interface for database backends.
backend_type
Database backend type enumeration.
backend_type type
Database backend type.
Database performance metrics.
size_t successful_queries
size_t transactions_started
double queries_per_second
bool enable_query_logging
Log all SQL queries executed.
bool enable_metrics
Enable metrics collection.
double connection_pool_utilization
std::vector< std::string > issues
std::size_t min_connections
Minimum number of connections to maintain.
std::size_t max_connections
Maximum number of connections allowed.
std::string to_sql_string() const
Get the value for SQL generation.
bool is_null() const noexcept
Check if this parameter represents a NULL value.
const std::string & get_value() const noexcept
Get the string value (returns empty string for null)
std::vector< row_data > rows
db_logger_config logger
Logger configuration.
pool_config connection_pool
Connection pool configuration.
db_monitoring_config monitoring
Monitoring configuration.
database_config database
Database connection configuration.
bool test_remove_success()
bool test_select_success()
bool test_connect_unsupported_backend()
bool test_execute_success()
bool test_begin_transaction_failure()
bool test_transaction_commit()
bool test_transaction_metrics()
bool test_move_semantics()
bool test_query_parameters()
bool test_transaction_is_active()
bool test_select_failure_not_connected()
bool test_thread_safety_metrics()
bool test_get_pool_stats()
bool test_begin_transaction_success()
bool test_health_check_structure()
#define ASSERT_TRUE(condition, message)
bool test_update_success()
bool test_insert_failure_not_connected()
bool test_reset_metrics()
bool test_get_backend_type()
bool test_connection_state_api()
bool test_create_query_builder()
bool test_transaction_rollback()
bool test_thread_safety_health_checks()
bool test_zero_config_construction()
bool test_transaction_execute()
#define ASSERT_FALSE(condition, message)
bool test_builder_custom()
bool test_metrics_update_on_query()
bool test_insert_success()
bool test_health_check_api()
bool test_builder_default()
bool test_remove_failure_not_connected()
bool test_query_param_null_safety()
bool test_connect_typed_backend()
bool test_error_handling_no_connection()
bool test_execute_transaction_success()
bool test_metrics_structure()
bool test_query_result_structure()
bool test_execute_transaction_failure()
bool test_update_failure_not_connected()
bool test_transaction_raii_cleanup()
bool test_config_construction()
bool test_connect_mock_backend()
Zero-configuration database system with integrated adapters (Phase 6)