Database System 0.1.0
Advanced C++20 Database System with Multi-Backend Support
Loading...
Searching...
No Matches
test_logger_adapter.cpp
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
13
14#include <chrono>
15#include <filesystem>
16#include <fstream>
17#include <iostream>
18#include <thread>
19#include <vector>
20
21using namespace database::integrated;
22using namespace database::integrated::adapters;
23
24namespace fs = std::filesystem;
25
26// Test result tracking
27static int tests_passed = 0;
28static int tests_failed = 0;
29
30#define TEST_ASSERT(condition, message) \
31 do { \
32 if (!(condition)) { \
33 std::cerr << " ✗ FAILED: " << message << "\n"; \
34 tests_failed++; \
35 return false; \
36 } \
37 } while (0)
38
39#define TEST_EXPECT_TRUE(condition, message) TEST_ASSERT(condition, message)
40#define TEST_EXPECT_FALSE(condition, message) TEST_ASSERT(!(condition), message)
41
42// ═══════════════════════════════════════════════════════════════
43// Test Helper Functions
44// ═══════════════════════════════════════════════════════════════
45
50 const std::string test_log_dir = "./test_logs";
51 if (fs::exists(test_log_dir)) {
52 fs::remove_all(test_log_dir);
53 }
54}
55
59bool log_file_contains(const std::string &log_dir, const std::string &pattern) {
60 if (!fs::exists(log_dir)) {
61 return false;
62 }
63
64 for (const auto &entry : fs::directory_iterator(log_dir)) {
65 if (entry.is_regular_file()) {
66 std::ifstream file(entry.path());
67 std::string line;
68 while (std::getline(file, line)) {
69 if (line.find(pattern) != std::string::npos) {
70 return true;
71 }
72 }
73 }
74 }
75 return false;
76}
77
78// ═══════════════════════════════════════════════════════════════
79// Test Cases
80// ═══════════════════════════════════════════════════════════════
81
86 std::cout << "\nTesting logger initialization and shutdown...\n";
87
89
90 db_logger_config config;
91 config.enable_file_logging = true;
92 config.log_directory = "./test_logs";
93 config.min_log_level = db_log_level::debug;
94
95 logger_adapter logger(config);
96
97 // Test initialization
98 auto init_result = logger.initialize();
99 TEST_EXPECT_TRUE(init_result.is_ok(),
100 "Logger should initialize successfully");
102 "Logger should be marked as initialized");
103
104 // Test double initialization (should be safe)
105 auto reinit_result = logger.initialize();
106 TEST_EXPECT_TRUE(reinit_result.is_ok(), "Re-initialization should be safe");
107
108 // Test shutdown
109 auto shutdown_result = logger.shutdown();
110 TEST_EXPECT_TRUE(shutdown_result.is_ok(),
111 "Logger should shutdown successfully");
113 "Logger should not be initialized after shutdown");
114
115 // Test double shutdown (should be safe)
116 auto reshutdown_result = logger.shutdown();
117 TEST_EXPECT_TRUE(reshutdown_result.is_ok(), "Re-shutdown should be safe");
118
119 std::cout << " ✓ Initialization and shutdown tests passed\n";
120 tests_passed++;
121 return true;
122}
123
128 std::cout << "\nTesting basic logging...\n";
129
131
132 db_logger_config config;
133 config.enable_file_logging = true;
134 config.log_directory = "./test_logs";
135 config.min_log_level = db_log_level::debug;
136
137 logger_adapter logger(config);
138 auto init_result = logger.initialize();
139 TEST_EXPECT_TRUE(init_result.is_ok(), "Logger should initialize");
140
141 // Log at different levels
142 logger.log(db_log_level::debug, "Debug message");
143 logger.log(db_log_level::info, "Info message");
144 logger.log(db_log_level::warning, "Warning message");
145 logger.log(db_log_level::error, "Error message");
146
147 // Flush to ensure logs are written
148 logger.flush();
149
150 // Give a small delay for async writes
151 std::this_thread::yield();
152
153 // Verify logs were written (if file logging is working)
154 if (fs::exists("./test_logs")) {
155 TEST_EXPECT_TRUE(log_file_contains("./test_logs", "Debug message") ||
156 log_file_contains("./test_logs", "Info message"),
157 "Log file should contain logged messages");
158 }
159
160 logger.shutdown();
161
162 std::cout << " ✓ Basic logging tests passed\n";
163 tests_passed++;
164 return true;
165}
166
171 std::cout << "\nTesting query logging and SQL sanitization...\n";
172
174
175 db_logger_config config;
176 config.enable_query_logging = true;
177 config.enable_file_logging = true;
178 config.log_directory = "./test_logs";
179 config.min_log_level = db_log_level::debug;
180
181 logger_adapter logger(config);
182 auto init_result = logger.initialize();
183 TEST_EXPECT_TRUE(init_result.is_ok(), "Logger should initialize");
184
185 // Test normal query logging
186 logger.log_query(db_log_level::info, "SELECT * FROM users WHERE id = 123",
187 std::chrono::microseconds(1500));
188
189 // Test query with password (should be sanitized)
190 logger.log_query(db_log_level::info,
191 "CREATE USER 'admin' IDENTIFIED BY PASSWORD 'secret123'",
192 std::chrono::microseconds(500));
193
194 // Test very long query (should be truncated)
195 std::string long_query = "SELECT ";
196 for (int i = 0; i < 1000; i++) {
197 long_query += "column" + std::to_string(i) + ", ";
198 }
199 long_query += "* FROM large_table";
200
201 logger.log_query(db_log_level::info, long_query,
202 std::chrono::microseconds(2000));
203
204 logger.flush();
205 std::this_thread::yield();
206
207 // Verify sanitization (password should not appear in logs)
208 if (fs::exists("./test_logs")) {
209 TEST_EXPECT_FALSE(log_file_contains("./test_logs", "secret123"),
210 "Password should be sanitized from logs");
211 }
212
213 logger.shutdown();
214
215 std::cout << " ✓ Query logging and sanitization tests passed\n";
216 tests_passed++;
217 return true;
218}
219
224 std::cout << "\nTesting slow query detection...\n";
225
227
228 db_logger_config config;
229 config.log_slow_queries = true;
230 config.slow_query_threshold = std::chrono::milliseconds(500);
231 config.enable_file_logging = true;
232 config.log_directory = "./test_logs";
233 config.min_log_level = db_log_level::debug;
234
235 logger_adapter logger(config);
236 auto init_result = logger.initialize();
237 TEST_EXPECT_TRUE(init_result.is_ok(), "Logger should initialize");
238
239 // Test fast query (should not trigger slow query warning)
240 logger.log_query(db_log_level::info, "SELECT * FROM fast_table",
241 std::chrono::microseconds(100000)); // 100ms
242
243 // Test slow query (should trigger warning)
244 logger.log_query(db_log_level::info, "SELECT * FROM slow_table",
245 std::chrono::microseconds(600000)); // 600ms
246
247 // Explicit slow query log
248 logger.log_slow_query("SELECT * FROM very_slow_table",
249 std::chrono::microseconds(1200000),
250 std::chrono::milliseconds(500));
251
252 logger.flush();
253 std::this_thread::yield();
254
255 // Verify slow query warning was logged
256 if (fs::exists("./test_logs")) {
257 TEST_EXPECT_TRUE(log_file_contains("./test_logs", "SLOW QUERY") ||
258 log_file_contains("./test_logs", "slow_table"),
259 "Slow query should be detected and logged");
260 }
261
262 logger.shutdown();
263
264 std::cout << " ✓ Slow query detection tests passed\n";
265 tests_passed++;
266 return true;
267}
268
273 std::cout << "\nTesting connection event logging...\n";
274
276
277 db_logger_config config;
278 config.enable_connection_logging = true;
279 config.enable_file_logging = true;
280 config.log_directory = "./test_logs";
281 config.min_log_level = db_log_level::debug;
282
283 logger_adapter logger(config);
284 auto init_result = logger.initialize();
285 TEST_EXPECT_TRUE(init_result.is_ok(), "Logger should initialize");
286
287 // Test various connection events
288 logger.log_connection_event("acquired", "Pool: main_pool, Priority: high");
289 logger.log_connection_event("released", "Pool: main_pool, Connection ID: 42");
290 logger.log_connection_event("timeout", "Pool: main_pool, Wait time: 30s");
291
292 // Test pool events
293 logger.log_pool_event("resized", 15, 5); // 15 active, 5 idle
294 logger.log_pool_event("shrunk", 10, 8); // 10 active, 8 idle
295 logger.log_pool_event("health_check", 12, 6);
296
297 logger.flush();
298 std::this_thread::yield();
299
300 logger.shutdown();
301
302 std::cout << " ✓ Connection event logging tests passed\n";
303 tests_passed++;
304 return true;
305}
306
311 std::cout << "\nTesting transaction logging...\n";
312
314
315 db_logger_config config;
316 config.enable_file_logging = true;
317 config.log_directory = "./test_logs";
318 config.min_log_level = db_log_level::debug;
319
320 logger_adapter logger(config);
321 auto init_result = logger.initialize();
322 TEST_EXPECT_TRUE(init_result.is_ok(), "Logger should initialize");
323
324 // Test successful transactions
325 logger.log_transaction("begin", true, "Isolation level: READ COMMITTED");
326 logger.log_transaction("commit", true, "");
327
328 // Test failed transaction
329 logger.log_transaction("rollback", false,
330 "Constraint violation: duplicate key");
331
332 logger.flush();
333 std::this_thread::yield();
334
335 logger.shutdown();
336
337 std::cout << " ✓ Transaction logging tests passed\n";
338 tests_passed++;
339 return true;
340}
341
346 std::cout << "\nTesting error logging...\n";
347
349
350 db_logger_config config;
351 config.enable_file_logging = true;
352 config.log_directory = "./test_logs";
353 config.min_log_level = db_log_level::debug;
354
355 logger_adapter logger(config);
356 auto init_result = logger.initialize();
357 TEST_EXPECT_TRUE(init_result.is_ok(), "Logger should initialize");
358
359 // Test various errors
360 logger.log_error("execute_query", "Connection lost", "08006");
361 logger.log_error("connect", "Authentication failed", "28000");
362 logger.log_error("prepare_statement", "Syntax error", "42601");
363
364 logger.flush();
365 std::this_thread::yield();
366
367 logger.shutdown();
368
369 std::cout << " ✓ Error logging tests passed\n";
370 tests_passed++;
371 return true;
372}
373
378 std::cout << "\nTesting thread safety...\n";
379
381
382 db_logger_config config;
383 config.enable_file_logging = true;
384 config.log_directory = "./test_logs";
385 config.min_log_level = db_log_level::debug;
386
387 logger_adapter logger(config);
388 auto init_result = logger.initialize();
389 TEST_EXPECT_TRUE(init_result.is_ok(), "Logger should initialize");
390
391 // Spawn multiple threads that log concurrently
392 const int num_threads = 4;
393 const int logs_per_thread = 50;
394 std::vector<std::thread> threads;
395
396 for (int t = 0; t < num_threads; t++) {
397 threads.emplace_back([&logger, t, logs_per_thread]() {
398 for (int i = 0; i < logs_per_thread; i++) {
399 logger.log(db_log_level::info, "Thread " + std::to_string(t) +
400 " message " + std::to_string(i));
401 }
402 });
403 }
404
405 // Wait for all threads to complete
406 for (auto &thread : threads) {
407 thread.join();
408 }
409
410 logger.flush();
411 std::this_thread::yield();
412
413 logger.shutdown();
414
415 std::cout << " ✓ Thread safety tests passed (no crashes)\n";
416 tests_passed++;
417 return true;
418}
419
424 std::cout << "\nTesting log level filtering...\n";
425
427
428 db_logger_config config;
429 config.enable_file_logging = true;
430 config.log_directory = "./test_logs";
431 config.min_log_level = db_log_level::warning; // Only warning and above
432
433 logger_adapter logger(config);
434 auto init_result = logger.initialize();
435 TEST_EXPECT_TRUE(init_result.is_ok(), "Logger should initialize");
436
437 // These should be filtered out
438 logger.log(db_log_level::trace, "Trace message - should not appear");
439 logger.log(db_log_level::debug, "Debug message - should not appear");
440 logger.log(db_log_level::info, "Info message - should not appear");
441
442 // These should appear
443 logger.log(db_log_level::warning, "Warning message - should appear");
444 logger.log(db_log_level::error, "Error message - should appear");
445 logger.log(db_log_level::critical, "Critical message - should appear");
446
447 logger.flush();
448 std::this_thread::yield();
449
450 // Verify filtering
451 if (fs::exists("./test_logs")) {
452 TEST_EXPECT_FALSE(log_file_contains("./test_logs", "should not appear"),
453 "Debug/Info logs should be filtered out");
454 TEST_EXPECT_TRUE(log_file_contains("./test_logs", "should appear") ||
455 log_file_contains("./test_logs", "Warning message"),
456 "Warning/Error logs should appear");
457 }
458
459 logger.shutdown();
460
461 std::cout << " ✓ Log level filtering tests passed\n";
462 tests_passed++;
463 return true;
464}
465
466// ═══════════════════════════════════════════════════════════════
467// Main Test Runner
468// ═══════════════════════════════════════════════════════════════
469
470int main() {
471 std::cout << "=== Running Logger Adapter Tests ===\n";
472 std::cout << "\nMode: Backend pattern with runtime selection\n";
473 std::cout
474 << "Backend will be auto-selected (common_logger -> fallback_logger)\n";
475
476 // Run all tests
486
487 // Cleanup
489
490 // Print summary
491 std::cout << "\n=== Test Summary ===\n";
492 std::cout << "Passed: " << tests_passed << "\n";
493 std::cout << "Failed: " << tests_failed << "\n";
494
495 if (tests_failed == 0) {
496 std::cout << "\n=== All tests passed! ✓ ===\n";
497 return 0;
498 } else {
499 std::cout << "\n=== Some tests failed! ✗ ===\n";
500 return 1;
501 }
502}
Unified logging adapter for database operations.
void log_query(db_log_level level, const std::string &query, std::chrono::microseconds duration)
Log a SQL query execution.
common::VoidResult initialize()
Initialize the logger.
void log_slow_query(const std::string &query, std::chrono::microseconds duration, std::chrono::milliseconds threshold)
Log a slow query with warning.
void log_transaction(const std::string &operation, bool success, const std::string &details)
Log a transaction operation.
void log_connection_event(const std::string &event, const std::string &details)
Log a connection pool event.
common::VoidResult shutdown()
Shutdown the logger gracefully.
void log_pool_event(const std::string &event, std::size_t active, std::size_t idle)
Log a connection pool state change.
void log(db_log_level level, const std::string &message)
Generic log message.
void flush()
Flush pending log messages.
void log_error(const std::string &operation, const std::string &error_msg, const std::string &sql_state="")
Log a database error.
bool is_initialized() const
Check if logger is initialized.
Database logging adapter with runtime backend selection.
bool enable_query_logging
Log all SQL queries executed.
bool enable_file_logging
Enable logging to file (in addition to console)
std::string log_directory
Directory for log files.
std::chrono::milliseconds slow_query_threshold
Threshold for considering a query "slow".
bool enable_connection_logging
Log connection pool events (acquire, release, etc.)
bool log_slow_queries
Automatically detect and log slow queries.
db_log_level min_log_level
Minimum log level to output.
bool test_query_logging()
Test query logging and SQL sanitization.
bool test_log_level_filtering()
Test log level filtering.
#define TEST_EXPECT_FALSE(condition, message)
bool test_error_logging()
Test error logging.
bool test_transaction_logging()
Test transaction logging.
bool test_connection_logging()
Test connection event logging.
bool test_initialization()
Test logger initialization and shutdown.
bool log_file_contains(const std::string &log_dir, const std::string &pattern)
Check if a log file contains a specific string.
bool test_slow_query_detection()
Test slow query detection.
void cleanup_test_logs()
Clean up test log directory.
static int tests_passed
bool test_basic_logging()
Test basic logging functionality.
static int tests_failed
bool test_thread_safety()
Test thread safety (basic concurrency test)
#define TEST_EXPECT_TRUE(condition, message)
int main()