17#include <gtest/gtest.h>
24using namespace std::chrono_literals;
30TEST(AlertSeverityTest, ToStringConversions) {
37TEST(AlertSeverityTest, OrderingByValue) {
38 EXPECT_LT(
static_cast<uint8_t
>(alert_severity::info),
39 static_cast<uint8_t
>(alert_severity::warning));
40 EXPECT_LT(
static_cast<uint8_t
>(alert_severity::warning),
41 static_cast<uint8_t
>(alert_severity::critical));
42 EXPECT_LT(
static_cast<uint8_t
>(alert_severity::critical),
43 static_cast<uint8_t
>(alert_severity::emergency));
50TEST(AlertStateTest, ToStringConversions) {
68 EXPECT_TRUE(labels_.labels.empty());
72 std::unordered_map<std::string, std::string> map{
73 {
"env",
"production"}, {
"service",
"api"}};
75 EXPECT_EQ(lbl.
labels.size(), 2u);
76 EXPECT_EQ(lbl.
get(
"env"),
"production");
80 labels_.set(
"team",
"infra");
81 EXPECT_EQ(labels_.get(
"team"),
"infra");
85 EXPECT_EQ(labels_.get(
"missing"),
"");
89 labels_.set(
"region",
"us-east");
90 EXPECT_TRUE(labels_.has(
"region"));
91 EXPECT_FALSE(labels_.has(
"zone"));
95 labels_.set(
"env",
"staging");
96 labels_.set(
"env",
"production");
97 EXPECT_EQ(labels_.get(
"env"),
"production");
101 labels_.set(
"b",
"2");
102 labels_.set(
"a",
"1");
103 auto fp1 = labels_.fingerprint();
108 auto fp2 =
other.fingerprint();
115 labels_.set(
"key",
"value1");
116 auto fp1 = labels_.fingerprint();
120 auto fp2 =
other.fingerprint();
126 labels_.set(
"a",
"1");
127 labels_.set(
"b",
"2");
133 EXPECT_EQ(labels_,
other);
137 labels_.set(
"a",
"1");
142 EXPECT_FALSE(labels_ ==
other);
149TEST(AlertAnnotationsTest, DefaultConstruction) {
151 EXPECT_TRUE(ann.
summary.empty());
154 EXPECT_TRUE(ann.
custom.empty());
157TEST(AlertAnnotationsTest, ConstructWithSummaryAndDescription) {
159 EXPECT_EQ(ann.
summary,
"High CPU");
160 EXPECT_EQ(ann.
description,
"CPU usage exceeded 80%");
163TEST(AlertAnnotationsTest, RunbookUrl) {
165 ann.
runbook_url =
"https://runbooks.example.com/cpu";
167 EXPECT_EQ(*ann.
runbook_url,
"https://runbooks.example.com/cpu");
170TEST(AlertAnnotationsTest, CustomAnnotations) {
172 ann.
custom[
"dashboard"] =
"grafana/cpu";
173 EXPECT_EQ(ann.
custom.at(
"dashboard"),
"grafana/cpu");
184 labels.
set(
"service",
"api");
185 labels.
set(
"env",
"prod");
186 alert a(
"high_cpu", labels);
187 a.
severity = alert_severity::critical;
195 EXPECT_TRUE(a.
name.empty());
196 EXPECT_EQ(a.
state, alert_state::inactive);
197 EXPECT_EQ(a.
severity, alert_severity::warning);
198 EXPECT_EQ(a.
value, 0.0);
204 auto a = create_test_alert();
205 EXPECT_EQ(a.name,
"high_cpu");
206 EXPECT_EQ(a.labels.get(
"service"),
"api");
207 EXPECT_EQ(a.severity, alert_severity::critical);
213 EXPECT_NE(a1.
id, a2.
id);
217 auto a = create_test_alert();
218 auto fp = a.fingerprint();
219 EXPECT_FALSE(fp.empty());
220 EXPECT_NE(fp.find(
"high_cpu"), std::string::npos);
224 auto a1 = create_test_alert();
225 auto a2 = create_test_alert();
227 EXPECT_EQ(a1.fingerprint(), a2.fingerprint());
234 a.
state = alert_state::pending;
237 a.
state = alert_state::firing;
240 a.
state = alert_state::resolved;
243 a.
state = alert_state::suppressed;
250 std::this_thread::sleep_for(1ms);
252 EXPECT_GT(dur.count(), 0);
270 EXPECT_TRUE(a_.transition_to(alert_state::pending));
271 EXPECT_EQ(a_.state, alert_state::pending);
275 EXPECT_FALSE(a_.transition_to(alert_state::firing));
276 EXPECT_EQ(a_.state, alert_state::inactive);
280 EXPECT_FALSE(a_.transition_to(alert_state::resolved));
281 EXPECT_EQ(a_.state, alert_state::inactive);
285 a_.transition_to(alert_state::pending);
286 EXPECT_TRUE(a_.transition_to(alert_state::firing));
287 EXPECT_EQ(a_.state, alert_state::firing);
288 EXPECT_TRUE(a_.started_at.has_value());
292 a_.transition_to(alert_state::pending);
293 EXPECT_TRUE(a_.transition_to(alert_state::inactive));
294 EXPECT_EQ(a_.state, alert_state::inactive);
298 a_.transition_to(alert_state::pending);
299 a_.transition_to(alert_state::firing);
300 EXPECT_TRUE(a_.transition_to(alert_state::resolved));
301 EXPECT_EQ(a_.state, alert_state::resolved);
302 EXPECT_TRUE(a_.resolved_at.has_value());
306 a_.transition_to(alert_state::pending);
307 a_.transition_to(alert_state::firing);
308 EXPECT_FALSE(a_.transition_to(alert_state::pending));
309 EXPECT_EQ(a_.state, alert_state::firing);
313 a_.transition_to(alert_state::pending);
314 a_.transition_to(alert_state::firing);
315 a_.transition_to(alert_state::resolved);
316 EXPECT_TRUE(a_.transition_to(alert_state::pending));
317 EXPECT_EQ(a_.state, alert_state::pending);
321 a_.transition_to(alert_state::pending);
322 a_.transition_to(alert_state::firing);
323 a_.transition_to(alert_state::resolved);
324 EXPECT_TRUE(a_.transition_to(alert_state::inactive));
328 EXPECT_TRUE(a_.transition_to(alert_state::suppressed));
329 EXPECT_EQ(a_.state, alert_state::suppressed);
333 a_.transition_to(alert_state::suppressed);
336 EXPECT_TRUE(a_.transition_to(alert_state::firing));
337 EXPECT_EQ(a_.state, alert_state::firing);
342 EXPECT_TRUE(a_.transition_to(alert_state::pending));
343 EXPECT_TRUE(a_.transition_to(alert_state::firing));
344 EXPECT_TRUE(a_.transition_to(alert_state::resolved));
345 EXPECT_TRUE(a_.transition_to(alert_state::pending));
346 EXPECT_TRUE(a_.transition_to(alert_state::firing));
347 EXPECT_TRUE(a_.transition_to(alert_state::resolved));
351 a_.transition_to(alert_state::pending);
352 a_.transition_to(alert_state::firing);
353 auto first_started = a_.started_at;
356 a_.transition_to(alert_state::resolved);
357 a_.transition_to(alert_state::pending);
358 a_.transition_to(alert_state::firing);
361 EXPECT_EQ(a_.started_at, first_started);
365 auto initial_updated = a_.updated_at;
366 std::this_thread::sleep_for(1ms);
367 a_.transition_to(alert_state::pending);
368 EXPECT_GT(a_.updated_at, initial_updated);
383 EXPECT_TRUE(g.
empty());
384 EXPECT_EQ(g.
size(), 0u);
388 EXPECT_EQ(group_.group_key,
"test_group");
389 EXPECT_TRUE(group_.empty());
395 EXPECT_EQ(group_.size(), 1u);
396 EXPECT_FALSE(group_.empty());
400 for (
int i = 0; i < 5; ++i) {
404 EXPECT_EQ(group_.size(), 5u);
408 EXPECT_EQ(group_.max_severity(), alert_severity::info);
413 a.
severity = alert_severity::critical;
415 EXPECT_EQ(group_.max_severity(), alert_severity::critical);
421 group_.add_alert(a1);
424 a2.
severity = alert_severity::emergency;
425 group_.add_alert(a2);
428 a3.
severity = alert_severity::warning;
429 group_.add_alert(a3);
431 EXPECT_EQ(group_.max_severity(), alert_severity::emergency);
435 auto initial = group_.updated_at;
436 std::this_thread::sleep_for(1ms);
439 EXPECT_GT(group_.updated_at, initial);
453 EXPECT_TRUE(silence_.is_active());
459 EXPECT_NE(s1.
id, s2.
id);
463 silence_.starts_at = std::chrono::steady_clock::now() - 2h;
464 silence_.ends_at = std::chrono::steady_clock::now() - 1h;
465 EXPECT_FALSE(silence_.is_active());
469 silence_.starts_at = std::chrono::steady_clock::now() + 1h;
470 silence_.ends_at = std::chrono::steady_clock::now() + 2h;
471 EXPECT_FALSE(silence_.is_active());
475 silence_.matchers.set(
"service",
"api");
481 EXPECT_TRUE(silence_.matches(a));
485 silence_.matchers.set(
"service",
"api");
490 EXPECT_FALSE(silence_.matches(a));
494 silence_.matchers.set(
"service",
"api");
499 EXPECT_FALSE(silence_.matches(a));
506 EXPECT_TRUE(silence_.matches(a));
510 silence_.matchers.set(
"service",
"api");
511 silence_.starts_at = std::chrono::steady_clock::now() - 2h;
512 silence_.ends_at = std::chrono::steady_clock::now() - 1h;
517 EXPECT_FALSE(silence_.matches(a));
521 silence_.matchers.set(
"service",
"api");
522 silence_.matchers.set(
"env",
"prod");
527 EXPECT_TRUE(silence_.matches(a1));
532 EXPECT_FALSE(silence_.matches(a2));
536 silence_.comment =
"Maintenance window";
537 silence_.created_by =
"admin@example.com";
538 EXPECT_EQ(silence_.comment,
"Maintenance window");
539 EXPECT_EQ(silence_.created_by,
"admin@example.com");
Core alert data structures for the monitoring system.
alert create_test_alert()
constexpr const char * alert_state_to_string(alert_state state) noexcept
Convert alert state to string.
constexpr const char * alert_severity_to_string(alert_severity severity) noexcept
Convert alert severity to string.
Additional metadata for alert context.
std::string description
Detailed description.
std::unordered_map< std::string, std::string > custom
Custom annotations.
std::string summary
Brief description.
std::optional< std::string > runbook_url
Link to runbook.
Group of related alerts for batch notification.
std::string group_key
Common grouping key.
bool empty() const
Check if group is empty.
size_t size() const
Get count of alerts in the group.
Key-value labels for alert identification and routing.
std::unordered_map< std::string, std::string > labels
void set(const std::string &key, const std::string &value)
Add or update a label.
std::string get(const std::string &key) const
Get a label value.
Silence configuration to suppress alerts.
Core alert data structure.
alert_state state
Current state.
std::optional< std::chrono::steady_clock::time_point > started_at
When firing started.
double value
Current metric value.
std::optional< std::chrono::steady_clock::time_point > resolved_at
When resolved.
alert_severity severity
Alert severity level.
std::chrono::steady_clock::duration state_duration() const
Get duration in current state.
uint64_t id
Unique alert ID.
std::string name
Alert name/identifier.
bool is_active() const
Check if alert is currently active (pending or firing)
alert_labels labels
Identifying labels.
std::chrono::steady_clock::duration firing_duration() const
Get firing duration (if currently firing)
TEST(AlertSeverityTest, ToStringConversions)
TEST_F(AlertLabelsTest, DefaultConstructionIsEmpty)