Database System 0.1.0
Advanced C++20 Database System with Multi-Backend Support
Loading...
Searching...
No Matches
test_performance_monitor.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
10#include <gtest/gtest.h>
11#include <chrono>
12#include <string>
13#include <vector>
14#include <atomic>
15#include <thread>
16
19
20using namespace database;
21using namespace database::monitoring;
22
23//=============================================================================
24// Helper: create a query_metrics with given parameters
25//=============================================================================
26
28 const std::string& hash,
29 std::chrono::microseconds exec_time,
30 bool success,
31 database_types db_type = database_types::postgres,
32 const std::string& error_msg = "")
33{
35 m.query_hash = hash;
36 m.start_time = std::chrono::steady_clock::now();
37 m.end_time = m.start_time + exec_time;
38 m.execution_time = exec_time;
39 m.success = success;
40 m.error_message = error_msg;
41 m.db_type = db_type;
42 m.rows_affected = success ? 1 : 0;
43 return m;
44}
45
46//=============================================================================
47// performance_monitor: record_query_metrics Tests
48//=============================================================================
49
50class PerformanceMonitorQueryTest : public ::testing::Test {
51protected:
52 std::shared_ptr<performance_monitor> monitor_;
53
54 void SetUp() override {
55 monitor_ = std::make_shared<performance_monitor>();
56 }
57};
58
59TEST_F(PerformanceMonitorQueryTest, RecordAndRetrieveSingleQuery) {
60 auto m = make_query("hash1", std::chrono::microseconds(5000), true);
61 monitor_->record_query_metrics(m);
62
63 auto summary = monitor_->get_performance_summary();
64 EXPECT_EQ(summary.total_queries, 1u);
65 EXPECT_EQ(summary.successful_queries, 1u);
66 EXPECT_EQ(summary.failed_queries, 0u);
67}
68
69TEST_F(PerformanceMonitorQueryTest, RecordMultipleQueries) {
70 monitor_->record_query_metrics(
71 make_query("q1", std::chrono::microseconds(1000), true));
72 monitor_->record_query_metrics(
73 make_query("q2", std::chrono::microseconds(2000), true));
74 monitor_->record_query_metrics(
75 make_query("q3", std::chrono::microseconds(3000), false,
76 database_types::postgres, "timeout"));
77
78 auto summary = monitor_->get_performance_summary();
79 EXPECT_EQ(summary.total_queries, 3u);
80 EXPECT_EQ(summary.successful_queries, 2u);
81 EXPECT_EQ(summary.failed_queries, 1u);
82 EXPECT_GT(summary.error_rate, 0.0);
83}
84
85TEST_F(PerformanceMonitorQueryTest, AvgQueryTimeCalculation) {
86 monitor_->record_query_metrics(
87 make_query("q1", std::chrono::microseconds(1000), true));
88 monitor_->record_query_metrics(
89 make_query("q2", std::chrono::microseconds(3000), true));
90
91 auto summary = monitor_->get_performance_summary();
92 EXPECT_EQ(summary.avg_query_time.count(), 2000);
93}
94
95//=============================================================================
96// performance_monitor: monitoring_enabled Tests
97//=============================================================================
98
99class MonitoringEnabledTest : public ::testing::Test {
100protected:
101 std::shared_ptr<performance_monitor> monitor_;
102
103 void SetUp() override {
104 monitor_ = std::make_shared<performance_monitor>();
105 }
106};
107
108TEST_F(MonitoringEnabledTest, DisabledDoesNotRecordQueryMetrics) {
109 monitor_->set_monitoring_enabled(false);
110 monitor_->record_query_metrics(
111 make_query("q1", std::chrono::microseconds(1000), true));
112
113 auto summary = monitor_->get_performance_summary();
114 EXPECT_EQ(summary.total_queries, 0u);
115}
116
117TEST_F(MonitoringEnabledTest, DisabledDoesNotRecordConnectionMetrics) {
118 monitor_->set_monitoring_enabled(false);
119
121 cm.total_connections.store(10);
122 cm.active_connections.store(5);
123 monitor_->record_connection_metrics(database_types::postgres, cm);
124
125 auto retrieved = monitor_->get_connection_metrics(database_types::postgres);
126 EXPECT_EQ(retrieved.total_connections.load(), 0u);
127}
128
129TEST_F(MonitoringEnabledTest, DisabledDoesNotUpdateConnectionCount) {
130 monitor_->set_monitoring_enabled(false);
131 monitor_->update_connection_count(database_types::sqlite, 5, 10);
132
133 auto retrieved = monitor_->get_connection_metrics(database_types::postgres);
134 EXPECT_EQ(retrieved.total_connections.load(), 0u);
135}
136
137TEST_F(MonitoringEnabledTest, ReEnableResumesRecording) {
138 monitor_->set_monitoring_enabled(false);
139 monitor_->record_query_metrics(
140 make_query("ignored", std::chrono::microseconds(100), true));
141
142 monitor_->set_monitoring_enabled(true);
143 monitor_->record_query_metrics(
144 make_query("recorded", std::chrono::microseconds(200), true));
145
146 auto summary = monitor_->get_performance_summary();
147 EXPECT_EQ(summary.total_queries, 1u);
148}
149
150//=============================================================================
151// performance_monitor: get_performance_summary(db_type) Tests
152//=============================================================================
153
154class FilteredSummaryTest : public ::testing::Test {
155protected:
156 std::shared_ptr<performance_monitor> monitor_;
157
158 void SetUp() override {
159 monitor_ = std::make_shared<performance_monitor>();
160 // Record queries for different database types
161 monitor_->record_query_metrics(
162 make_query("pg1", std::chrono::microseconds(1000), true, database_types::postgres));
163 monitor_->record_query_metrics(
164 make_query("pg2", std::chrono::microseconds(2000), true, database_types::postgres));
165 monitor_->record_query_metrics(
166 make_query("sq1", std::chrono::microseconds(3000), true, database_types::sqlite));
167 monitor_->record_query_metrics(
168 make_query("sq2", std::chrono::microseconds(4000), false,
169 database_types::sqlite, "connection lost"));
170 }
171};
172
173TEST_F(FilteredSummaryTest, PostgresSummaryFiltersCorrectly) {
174 auto summary = monitor_->get_performance_summary(database_types::postgres);
175 EXPECT_EQ(summary.total_queries, 2u);
176 EXPECT_EQ(summary.successful_queries, 2u);
177 EXPECT_EQ(summary.failed_queries, 0u);
178}
179
180TEST_F(FilteredSummaryTest, SqliteSummaryFiltersCorrectly) {
181 auto summary = monitor_->get_performance_summary(database_types::sqlite);
182 EXPECT_EQ(summary.total_queries, 2u);
183 EXPECT_EQ(summary.successful_queries, 1u);
184 EXPECT_EQ(summary.failed_queries, 1u);
185 EXPECT_DOUBLE_EQ(summary.error_rate, 0.5);
186}
187
188TEST_F(FilteredSummaryTest, UnusedDbTypeReturnsZero) {
189 auto summary = monitor_->get_performance_summary(database_types::mongodb);
190 EXPECT_EQ(summary.total_queries, 0u);
191}
192
193TEST_F(FilteredSummaryTest, OverallSummaryIncludesAll) {
194 auto summary = monitor_->get_performance_summary();
195 EXPECT_EQ(summary.total_queries, 4u);
196}
197
198//=============================================================================
199// performance_monitor: get_recent_queries / get_slow_queries Tests
200//=============================================================================
201
202class QueryHistoryTest : public ::testing::Test {
203protected:
204 std::shared_ptr<performance_monitor> monitor_;
205
206 void SetUp() override {
207 monitor_ = std::make_shared<performance_monitor>();
208 }
209};
210
211TEST_F(QueryHistoryTest, GetRecentQueriesWithinWindow) {
212 monitor_->record_query_metrics(
213 make_query("q1", std::chrono::microseconds(100), true));
214 monitor_->record_query_metrics(
215 make_query("q2", std::chrono::microseconds(200), true));
216
217 auto recent = monitor_->get_recent_queries(std::chrono::minutes(5));
218 EXPECT_EQ(recent.size(), 2u);
219}
220
221TEST_F(QueryHistoryTest, GetRecentQueriesEmptyWindow) {
222 monitor_->record_query_metrics(
223 make_query("q1", std::chrono::microseconds(100), true));
224
225 // Use zero-length window — queries just recorded should still be within
226 // the window since their start_time is "now"
227 auto recent = monitor_->get_recent_queries(std::chrono::minutes(0));
228 // With 0-minute window, cutoff == now, so queries at exactly now are borderline
229 // Result depends on timing; we just verify no crash
230 EXPECT_LE(recent.size(), 1u);
231}
232
233TEST_F(QueryHistoryTest, GetSlowQueriesAboveThreshold) {
234 monitor_->record_query_metrics(
235 make_query("fast", std::chrono::microseconds(100), true));
236 monitor_->record_query_metrics(
237 make_query("medium", std::chrono::microseconds(5000), true));
238 monitor_->record_query_metrics(
239 make_query("slow", std::chrono::microseconds(50000), true));
240
241 auto slow = monitor_->get_slow_queries(std::chrono::microseconds(4000));
242 EXPECT_EQ(slow.size(), 2u);
243}
244
245TEST_F(QueryHistoryTest, GetSlowQueriesNoneAboveThreshold) {
246 monitor_->record_query_metrics(
247 make_query("fast", std::chrono::microseconds(100), true));
248
249 auto slow = monitor_->get_slow_queries(std::chrono::microseconds(1000000));
250 EXPECT_TRUE(slow.empty());
251}
252
253TEST_F(QueryHistoryTest, GetSlowQueriesPreservesMetadata) {
254 auto original = make_query("slow_hash", std::chrono::microseconds(99999), false,
255 database_types::sqlite, "timeout");
256 monitor_->record_query_metrics(original);
257
258 auto slow = monitor_->get_slow_queries(std::chrono::microseconds(1000));
259 ASSERT_EQ(slow.size(), 1u);
260 EXPECT_EQ(slow[0].query_hash, "slow_hash");
261 EXPECT_FALSE(slow[0].success);
262 EXPECT_EQ(slow[0].error_message, "timeout");
263 EXPECT_EQ(slow[0].db_type, database_types::sqlite);
264}
265
266//=============================================================================
267// performance_monitor: Alert System Tests
268//=============================================================================
269
270class AlertSystemTest : public ::testing::Test {
271protected:
272 std::shared_ptr<performance_monitor> monitor_;
273
274 void SetUp() override {
275 monitor_ = std::make_shared<performance_monitor>();
276 }
277};
278
279TEST_F(AlertSystemTest, SlowQueryTriggersAlert) {
280 // Set low latency threshold so our query triggers an alert
281 monitor_->set_alert_thresholds(0.05, std::chrono::microseconds(100));
282
283 monitor_->record_query_metrics(
284 make_query("slow", std::chrono::microseconds(500), true));
285
286 auto alerts = monitor_->get_recent_alerts(std::chrono::minutes(5));
287 ASSERT_GE(alerts.size(), 1u);
288
289 bool found_slow_query_alert = false;
290 for (const auto& alert : alerts) {
291 if (alert.type() == performance_alert::alert_type::slow_query) {
292 found_slow_query_alert = true;
293 EXPECT_FALSE(alert.message().empty());
294 }
295 }
296 EXPECT_TRUE(found_slow_query_alert);
297}
298
299TEST_F(AlertSystemTest, RegisterAlertHandlerReceivesCallback) {
300 std::atomic<int> callback_count{0};
301 monitor_->register_alert_handler([&callback_count](const performance_alert&) {
302 callback_count.fetch_add(1);
303 });
304
305 // Trigger alert via slow query
306 monitor_->set_alert_thresholds(0.05, std::chrono::microseconds(10));
307 monitor_->record_query_metrics(
308 make_query("slow", std::chrono::microseconds(1000), true));
309
310 EXPECT_GE(callback_count.load(), 1);
311}
312
313TEST_F(AlertSystemTest, MultipleHandlersAllCalled) {
314 std::atomic<int> handler1_count{0};
315 std::atomic<int> handler2_count{0};
316
317 monitor_->register_alert_handler([&handler1_count](const performance_alert&) {
318 handler1_count.fetch_add(1);
319 });
320 monitor_->register_alert_handler([&handler2_count](const performance_alert&) {
321 handler2_count.fetch_add(1);
322 });
323
324 monitor_->set_alert_thresholds(0.05, std::chrono::microseconds(10));
325 monitor_->record_query_metrics(
326 make_query("slow", std::chrono::microseconds(1000), true));
327
328 EXPECT_GE(handler1_count.load(), 1);
329 EXPECT_GE(handler2_count.load(), 1);
330}
331
332TEST_F(AlertSystemTest, GetRecentAlertsEmptyByDefault) {
333 auto alerts = monitor_->get_recent_alerts(std::chrono::minutes(5));
334 EXPECT_TRUE(alerts.empty());
335}
336
337TEST_F(AlertSystemTest, RecordSlowQueryEmitsAlert) {
338 monitor_->record_slow_query("SELECT * FROM huge_table",
339 std::chrono::microseconds(5000000));
340
341 auto alerts = monitor_->get_recent_alerts(std::chrono::minutes(5));
342 ASSERT_GE(alerts.size(), 1u);
343 EXPECT_EQ(alerts.back().type(), performance_alert::alert_type::slow_query);
344}
345
346TEST_F(AlertSystemTest, ConnectionPoolExhaustionAlert) {
348 cm.total_connections.store(10);
349 cm.active_connections.store(10); // 100% utilization > 90% threshold
350
351 monitor_->record_connection_metrics(database_types::postgres, cm);
352
353 auto alerts = monitor_->get_recent_alerts(std::chrono::minutes(5));
354 bool found_pool_alert = false;
355 for (const auto& alert : alerts) {
356 if (alert.type() == performance_alert::alert_type::connection_pool_exhaustion) {
357 found_pool_alert = true;
358 }
359 }
360 EXPECT_TRUE(found_pool_alert);
361}
362
363//=============================================================================
364// performance_monitor: Metric Lifecycle Tests
365//=============================================================================
366
367class MetricLifecycleTest : public ::testing::Test {
368protected:
369 std::shared_ptr<performance_monitor> monitor_;
370
371 void SetUp() override {
372 monitor_ = std::make_shared<performance_monitor>();
373 }
374};
375
376TEST_F(MetricLifecycleTest, ClearMetricsRemovesEverything) {
377 monitor_->record_query_metrics(
378 make_query("q1", std::chrono::microseconds(100), true));
379 monitor_->update_connection_count(database_types::postgres, 5, 10);
380 monitor_->record_slow_query("SELECT 1", std::chrono::microseconds(999999));
381
382 monitor_->clear_metrics();
383
384 auto summary = monitor_->get_performance_summary();
385 EXPECT_EQ(summary.total_queries, 0u);
386 EXPECT_EQ(summary.total_connections, 0u);
387
388 auto alerts = monitor_->get_recent_alerts(std::chrono::minutes(60));
389 EXPECT_TRUE(alerts.empty());
390}
391
392TEST_F(MetricLifecycleTest, CleanupOldMetricsRemovesExpired) {
393 // Set very short retention period
394 monitor_->set_metrics_retention_period(std::chrono::minutes(0));
395
396 monitor_->record_query_metrics(
397 make_query("old", std::chrono::microseconds(100), true));
398
399 monitor_->cleanup_old_metrics();
400
401 auto summary = monitor_->get_performance_summary();
402 EXPECT_EQ(summary.total_queries, 0u);
403}
404
405TEST_F(MetricLifecycleTest, CleanupKeepsRecentMetrics) {
406 monitor_->set_metrics_retention_period(std::chrono::minutes(60));
407
408 monitor_->record_query_metrics(
409 make_query("recent", std::chrono::microseconds(100), true));
410
411 monitor_->cleanup_old_metrics();
412
413 auto summary = monitor_->get_performance_summary();
414 EXPECT_EQ(summary.total_queries, 1u);
415}
416
417//=============================================================================
418// performance_monitor: Connection Metrics Tests
419//=============================================================================
420
421class ConnectionMetricsTest : public ::testing::Test {
422protected:
423 std::shared_ptr<performance_monitor> monitor_;
424
425 void SetUp() override {
426 monitor_ = std::make_shared<performance_monitor>();
427 }
428};
429
430TEST_F(ConnectionMetricsTest, UpdateConnectionCountStoresValues) {
431 monitor_->update_connection_count(database_types::postgres, 5, 10);
432
433 auto cm = monitor_->get_connection_metrics(database_types::postgres);
434 EXPECT_EQ(cm.active_connections.load(), 5u);
435 EXPECT_EQ(cm.total_connections.load(), 10u);
436}
437
438TEST_F(ConnectionMetricsTest, GetConnectionMetricsUnknownDbType) {
439 auto cm = monitor_->get_connection_metrics(database_types::sqlite);
440 EXPECT_EQ(cm.total_connections.load(), 0u);
441 EXPECT_EQ(cm.active_connections.load(), 0u);
442}
443
444TEST_F(ConnectionMetricsTest, ConnectionSummaryIncludesMultipleTypes) {
445 // get_performance_summary() requires at least one query in history
446 // to reach connection metrics aggregation (early return if empty)
447 monitor_->record_query_metrics(
448 make_query("q1", std::chrono::microseconds(100), true));
449
450 monitor_->update_connection_count(database_types::postgres, 3, 10);
451 monitor_->update_connection_count(database_types::sqlite, 2, 5);
452
453 auto summary = monitor_->get_performance_summary();
454 EXPECT_EQ(summary.total_connections, 15u);
455 EXPECT_EQ(summary.active_connections, 5u);
456 EXPECT_DOUBLE_EQ(summary.connection_utilization, 5.0 / 15.0);
457}
458
459//=============================================================================
460// performance_monitor: JSON and Dashboard Tests
461//=============================================================================
462
463class MetricsOutputTest : public ::testing::Test {
464protected:
465 std::shared_ptr<performance_monitor> monitor_;
466
467 void SetUp() override {
468 monitor_ = std::make_shared<performance_monitor>();
469 }
470};
471
472TEST_F(MetricsOutputTest, GetMetricsJsonContainsFields) {
473 monitor_->record_query_metrics(
474 make_query("q1", std::chrono::microseconds(1000), true));
475
476 std::string json = monitor_->get_metrics_json();
477 EXPECT_NE(json.find("total_queries"), std::string::npos);
478 EXPECT_NE(json.find("successful_queries"), std::string::npos);
479 EXPECT_NE(json.find("failed_queries"), std::string::npos);
480 EXPECT_NE(json.find("avg_query_time_us"), std::string::npos);
481 EXPECT_NE(json.find("error_rate"), std::string::npos);
482 EXPECT_NE(json.find("active_connections"), std::string::npos);
483}
484
485TEST_F(MetricsOutputTest, GetMetricsJsonEmptyMonitor) {
486 std::string json = monitor_->get_metrics_json();
487 EXPECT_FALSE(json.empty());
488 EXPECT_NE(json.find("total_queries"), std::string::npos);
489}
490
491//=============================================================================
492// performance_alert Tests
493//=============================================================================
494
495class PerformanceAlertTest : public ::testing::Test {};
496
497TEST_F(PerformanceAlertTest, ConstructionAndAccessors) {
498 auto now = std::chrono::steady_clock::now();
499 performance_alert alert(
500 performance_alert::alert_type::high_latency,
501 "Latency too high",
502 now);
503
504 EXPECT_EQ(alert.type(), performance_alert::alert_type::high_latency);
505 EXPECT_EQ(alert.message(), "Latency too high");
506 EXPECT_EQ(alert.timestamp(), now);
507}
508
510 auto now = std::chrono::steady_clock::now();
511 const performance_alert::alert_type types[] = {
512 performance_alert::alert_type::high_latency,
513 performance_alert::alert_type::high_error_rate,
514 performance_alert::alert_type::connection_pool_exhaustion,
515 performance_alert::alert_type::slow_query,
516 performance_alert::alert_type::memory_usage,
517 performance_alert::alert_type::cpu_usage
518 };
519
520 for (auto type : types) {
521 performance_alert alert(type, "test", now);
522 EXPECT_EQ(alert.type(), type);
523 }
524}
525
526//=============================================================================
527// pool_metrics Tests
528//=============================================================================
529
530class PoolMetricsTest : public ::testing::Test {
531protected:
533};
534
535TEST_F(PoolMetricsTest, InitialStateIsZero) {
536 EXPECT_EQ(metrics_.total_acquisitions.load(), 0u);
537 EXPECT_EQ(metrics_.successful_acquisitions.load(), 0u);
538 EXPECT_EQ(metrics_.failed_acquisitions.load(), 0u);
539 EXPECT_EQ(metrics_.timeouts.load(), 0u);
540 EXPECT_EQ(metrics_.current_active.load(), 0u);
541 EXPECT_EQ(metrics_.current_queued.load(), 0u);
542}
543
544TEST_F(PoolMetricsTest, RecordSuccessfulAcquisition) {
545 metrics_.record_acquisition(500, true);
546
547 EXPECT_EQ(metrics_.total_acquisitions.load(), 1u);
548 EXPECT_EQ(metrics_.successful_acquisitions.load(), 1u);
549 EXPECT_EQ(metrics_.failed_acquisitions.load(), 0u);
550 EXPECT_EQ(metrics_.total_wait_time_us.load(), 500u);
551 EXPECT_EQ(metrics_.min_wait_time_us.load(), 500u);
552 EXPECT_EQ(metrics_.max_wait_time_us.load(), 500u);
553}
554
555TEST_F(PoolMetricsTest, RecordFailedAcquisition) {
556 metrics_.record_acquisition(0, false);
557
558 EXPECT_EQ(metrics_.total_acquisitions.load(), 1u);
559 EXPECT_EQ(metrics_.successful_acquisitions.load(), 0u);
560 EXPECT_EQ(metrics_.failed_acquisitions.load(), 1u);
561}
562
563TEST_F(PoolMetricsTest, RecordMultipleAcquisitionsTracksMinMax) {
564 metrics_.record_acquisition(100, true);
565 metrics_.record_acquisition(500, true);
566 metrics_.record_acquisition(200, true);
567
568 EXPECT_EQ(metrics_.total_acquisitions.load(), 3u);
569 EXPECT_EQ(metrics_.successful_acquisitions.load(), 3u);
570 EXPECT_EQ(metrics_.total_wait_time_us.load(), 800u);
571 EXPECT_EQ(metrics_.min_wait_time_us.load(), 100u);
572 EXPECT_EQ(metrics_.max_wait_time_us.load(), 500u);
573}
574
575TEST_F(PoolMetricsTest, RecordTimeout) {
576 metrics_.record_timeout();
577 metrics_.record_timeout();
578
579 EXPECT_EQ(metrics_.timeouts.load(), 2u);
580}
581
582TEST_F(PoolMetricsTest, UpdateActiveTracksPeak) {
583 metrics_.update_active(1);
584 metrics_.update_active(1);
585 metrics_.update_active(1); // peak = 3
586 metrics_.update_active(-1); // current = 2
587
588 EXPECT_EQ(metrics_.current_active.load(), 2u);
589 EXPECT_EQ(metrics_.peak_active.load(), 3u);
590}
591
592TEST_F(PoolMetricsTest, UpdateQueuedTracksPeak) {
593 metrics_.update_queued(1);
594 metrics_.update_queued(1);
595 metrics_.update_queued(1); // peak = 3
596 metrics_.update_queued(-1);
597 metrics_.update_queued(-1); // current = 1
598
599 EXPECT_EQ(metrics_.current_queued.load(), 1u);
600 EXPECT_EQ(metrics_.peak_queued.load(), 3u);
601}
602
603TEST_F(PoolMetricsTest, RecordHealthCheck) {
604 metrics_.record_health_check(0);
605 metrics_.record_health_check(2);
606 metrics_.record_health_check(1);
607
608 EXPECT_EQ(metrics_.health_checks_performed.load(), 3u);
609 EXPECT_EQ(metrics_.unhealthy_connections_removed.load(), 3u);
610}
611
612TEST_F(PoolMetricsTest, AverageWaitTime) {
613 metrics_.record_acquisition(100, true);
614 metrics_.record_acquisition(300, true);
615
616 // average_wait_time_us divides by total_acquisitions (2), not successful
617 EXPECT_DOUBLE_EQ(metrics_.average_wait_time_us(), 200.0);
618}
619
620TEST_F(PoolMetricsTest, AverageWaitTimeZeroWhenNoAcquisitions) {
621 EXPECT_DOUBLE_EQ(metrics_.average_wait_time_us(), 0.0);
622}
623
624TEST_F(PoolMetricsTest, SuccessRate) {
625 metrics_.record_acquisition(100, true);
626 metrics_.record_acquisition(200, true);
627 metrics_.record_acquisition(0, false);
628
629 EXPECT_NEAR(metrics_.success_rate(), 66.666, 0.01);
630}
631
632TEST_F(PoolMetricsTest, SuccessRateNoAcquisitions) {
633 EXPECT_DOUBLE_EQ(metrics_.success_rate(), 100.0);
634}
635
636TEST_F(PoolMetricsTest, ResetClearsStatistics) {
637 metrics_.record_acquisition(500, true);
638 metrics_.record_timeout();
639 metrics_.update_active(3);
640 metrics_.record_health_check(1);
641
642 metrics_.reset();
643
644 EXPECT_EQ(metrics_.total_acquisitions.load(), 0u);
645 EXPECT_EQ(metrics_.successful_acquisitions.load(), 0u);
646 EXPECT_EQ(metrics_.timeouts.load(), 0u);
647 EXPECT_EQ(metrics_.total_wait_time_us.load(), 0u);
648 EXPECT_EQ(metrics_.min_wait_time_us.load(), UINT64_MAX);
649 EXPECT_EQ(metrics_.max_wait_time_us.load(), 0u);
650 EXPECT_EQ(metrics_.health_checks_performed.load(), 0u);
651 // current_active should remain (reflects current state)
652 EXPECT_EQ(metrics_.current_active.load(), 3u);
653 // peak_active reset to current_active
654 EXPECT_EQ(metrics_.peak_active.load(), 3u);
655}
656
657//=============================================================================
658// prometheus_exporter Tests
659//=============================================================================
660
661class PrometheusExporterTest : public ::testing::Test {};
662
663TEST_F(PrometheusExporterTest, FormatContainsMetricNames) {
664 prometheus_exporter exporter("localhost", 9090);
665 performance_summary summary;
666 summary.total_queries = 100;
667 summary.avg_query_time = std::chrono::microseconds(500);
668 summary.error_rate = 0.02;
669 summary.active_connections = 5;
670
671 std::string output = exporter.format_prometheus_metrics(summary);
672 EXPECT_NE(output.find("database_queries_total 100"), std::string::npos);
673 EXPECT_NE(output.find("database_query_duration_microseconds 500"), std::string::npos);
674 EXPECT_NE(output.find("database_error_rate 0.02"), std::string::npos);
675 EXPECT_NE(output.find("database_connections_active 5"), std::string::npos);
676}
677
678TEST_F(PrometheusExporterTest, ExportMetricsReturnsTrue) {
679 prometheus_exporter exporter("localhost", 9090);
680 performance_summary summary;
681
682 EXPECT_TRUE(exporter.export_metrics(summary));
683}
684
685TEST_F(PrometheusExporterTest, ExportAlertsReturnsTrue) {
686 prometheus_exporter exporter("localhost", 9090);
687 std::vector<performance_alert> alerts;
688 alerts.emplace_back(
689 performance_alert::alert_type::slow_query, "test",
690 std::chrono::steady_clock::now());
691
692 EXPECT_TRUE(exporter.export_alerts(alerts));
693}
694
695//=============================================================================
696// query_timer RAII Tests
697//=============================================================================
698
699class QueryTimerTest : public ::testing::Test {
700protected:
701 std::shared_ptr<performance_monitor> monitor_;
702
703 void SetUp() override {
704 monitor_ = std::make_shared<performance_monitor>();
705 }
706};
707
708TEST_F(QueryTimerTest, DestructorRecordsMetrics) {
709 {
710 query_timer timer("SELECT 1", database_types::postgres, monitor_);
711 // Timer will record on destruction
712 }
713
714 auto summary = monitor_->get_performance_summary();
715 EXPECT_EQ(summary.total_queries, 1u);
716 EXPECT_EQ(summary.successful_queries, 1u);
717}
718
719TEST_F(QueryTimerTest, SetErrorMarksFailure) {
720 {
721 query_timer timer("SELECT bad", database_types::postgres, monitor_);
722 timer.set_error("syntax error");
723 }
724
725 auto summary = monitor_->get_performance_summary();
726 EXPECT_EQ(summary.total_queries, 1u);
727 EXPECT_EQ(summary.failed_queries, 1u);
728}
729
730TEST_F(QueryTimerTest, SetRowsAffected) {
731 {
732 query_timer timer("INSERT INTO test VALUES (1)", database_types::sqlite, monitor_);
733 timer.set_rows_affected(42);
734 }
735
736 auto recent = monitor_->get_recent_queries(std::chrono::minutes(5));
737 ASSERT_EQ(recent.size(), 1u);
738 EXPECT_EQ(recent[0].rows_affected, 42u);
739}
std::shared_ptr< performance_monitor > monitor_
std::shared_ptr< performance_monitor > monitor_
std::shared_ptr< performance_monitor > monitor_
std::shared_ptr< performance_monitor > monitor_
std::shared_ptr< performance_monitor > monitor_
std::shared_ptr< performance_monitor > monitor_
std::shared_ptr< performance_monitor > monitor_
std::shared_ptr< performance_monitor > monitor_
std::shared_ptr< performance_monitor > monitor_
Alert system for performance thresholds.
std::chrono::steady_clock::time_point timestamp() const
Export metrics in Prometheus format.
std::string format_prometheus_metrics(const performance_summary &summary) const
bool export_alerts(const std::vector< performance_alert > &alerts) override
bool export_metrics(const performance_summary &summary) override
RAII timer for measuring query execution time.
void set_error(const std::string &error)
database_types
Represents various database backends or modes.
Metrics for database connection usage.
Performance metrics for connection pools.
Metrics for individual query execution.
std::chrono::steady_clock::time_point start_time
std::chrono::steady_clock::time_point end_time
std::chrono::microseconds execution_time
#define ASSERT_EQ(expected, actual, message)
static query_metrics make_query(const std::string &hash, std::chrono::microseconds exec_time, bool success, database_types db_type=database_types::postgres, const std::string &error_msg="")
TEST_F(PerformanceMonitorQueryTest, RecordAndRetrieveSingleQuery)