1package v2 2 3import ( 4 "encoding/json" 5 "sort" 6 "testing" 7 time "time" 8 9 "github.com/stretchr/testify/assert" 10 "github.com/stretchr/testify/require" 11) 12 13func TestFixtureEventIsValid(t *testing.T) { 14 e := FixtureEvent("entity", "check") 15 assert.NotNil(t, e) 16 assert.NotNil(t, e.Entity) 17 assert.NotNil(t, e.Check) 18} 19 20func TestEventValidate(t *testing.T) { 21 event := FixtureEvent("entity", "check") 22 23 event.Check.Name = "" 24 assert.Error(t, event.Validate()) 25 event.Check.Name = "check" 26 27 event.Entity.Name = "" 28 assert.Error(t, event.Validate()) 29 event.Entity.Name = "entity" 30 31 assert.NoError(t, event.Validate()) 32} 33 34func TestEventValidateNoTimestamp(t *testing.T) { 35 // Events without a timestamp are valid 36 event := FixtureEvent("entity", "check") 37 event.Timestamp = 0 38 if err := event.Validate(); err != nil { 39 t.Fatal(err) 40 } 41} 42 43func TestMarshalJSON(t *testing.T) { 44 event := FixtureEvent("entity", "check") 45 _, err := json.Marshal(event) 46 require.NoError(t, err) 47} 48 49func TestEventHasMetrics(t *testing.T) { 50 testCases := []struct { 51 name string 52 metrics *Metrics 53 expected bool 54 }{ 55 { 56 name: "No Metrics", 57 metrics: nil, 58 expected: false, 59 }, 60 { 61 name: "Metrics", 62 metrics: &Metrics{}, 63 expected: true, 64 }, 65 } 66 67 for _, tc := range testCases { 68 t.Run(tc.name, func(t *testing.T) { 69 event := &Event{ 70 Metrics: tc.metrics, 71 } 72 metrics := event.HasMetrics() 73 assert.Equal(t, tc.expected, metrics) 74 }) 75 } 76} 77 78func TestEventIsIncident(t *testing.T) { 79 testCases := []struct { 80 name string 81 status uint32 82 expected bool 83 }{ 84 { 85 name: "OK Status", 86 status: 0, 87 expected: false, 88 }, 89 { 90 name: "Non-zero Status", 91 status: 1, 92 expected: true, 93 }, 94 } 95 96 for _, tc := range testCases { 97 t.Run(tc.name, func(t *testing.T) { 98 event := &Event{ 99 Check: &Check{ 100 Status: tc.status, 101 }, 102 } 103 incident := event.IsIncident() 104 assert.Equal(t, tc.expected, incident) 105 }) 106 } 107} 108 109func TestEventIsResolution(t *testing.T) { 110 testCases := []struct { 111 name string 112 history []CheckHistory 113 status uint32 114 expected bool 115 }{ 116 { 117 name: "check has no history", 118 history: []CheckHistory{CheckHistory{}}, 119 status: 0, 120 expected: false, 121 }, 122 { 123 name: "check has not transitioned", 124 history: []CheckHistory{ 125 CheckHistory{Status: 1}, 126 CheckHistory{Status: 0}, 127 }, 128 status: 0, 129 expected: true, 130 }, 131 { 132 name: "check has just transitioned", 133 history: []CheckHistory{ 134 CheckHistory{Status: 0}, 135 CheckHistory{Status: 1}, 136 }, 137 status: 0, 138 expected: false, 139 }, 140 { 141 name: "check has transitioned but still an incident", 142 history: []CheckHistory{ 143 CheckHistory{Status: 2}, 144 CheckHistory{Status: 1}, 145 }, 146 status: 1, 147 expected: false, 148 }, 149 } 150 151 for _, tc := range testCases { 152 t.Run(tc.name, func(t *testing.T) { 153 event := &Event{ 154 Check: &Check{ 155 History: tc.history, 156 Status: tc.status, 157 }, 158 } 159 resolution := event.IsResolution() 160 assert.Equal(t, tc.expected, resolution) 161 }) 162 } 163} 164 165func TestEventIsSilenced(t *testing.T) { 166 testCases := []struct { 167 name string 168 event *Event 169 silenced []string 170 expected bool 171 }{ 172 { 173 name: "No silenced entries", 174 event: FixtureEvent("entity1", "check1"), 175 silenced: []string{}, 176 expected: false, 177 }, 178 { 179 name: "Silenced entry", 180 event: FixtureEvent("entity1", "check1"), 181 silenced: []string{"entity1"}, 182 expected: true, 183 }, 184 { 185 name: "Metric without a check", 186 event: &Event{}, 187 silenced: []string{"entity1"}, 188 expected: false, 189 }, 190 } 191 192 for _, tc := range testCases { 193 t.Run(tc.name, func(t *testing.T) { 194 if tc.event.Check != nil { 195 tc.event.Check.Silenced = tc.silenced 196 } 197 silenced := tc.event.IsSilenced() 198 assert.Equal(t, tc.expected, silenced) 199 }) 200 } 201} 202 203func TestEventsBySeverity(t *testing.T) { 204 critical := FixtureEvent("entity", "check") 205 critical.Check.Status = 2 // crit 206 warn := FixtureEvent("entity", "check") 207 warn.Check.Status = 1 // warn 208 unknown := FixtureEvent("entity", "check") 209 unknown.Check.Status = 3 // unknown 210 ok := FixtureEvent("entity", "check") 211 ok.Check.Status = 0 // ok 212 ok.Check.LastOK = 42 213 okOlder := FixtureEvent("entity", "check") 214 okOlder.Check.Status = 0 // ok 215 okOlder.Check.LastOK = 7 216 okOlderDiff := FixtureEvent("entity", "check") 217 okOlderDiff.Check.Status = 0 // ok 218 okOlderDiff.Check.LastOK = 7 219 okOlderDiff.Entity.Name = "zzz" 220 noCheck := FixtureEvent("entity", "check") 221 noCheck.Check = nil 222 223 testCases := []struct { 224 name string 225 input []*Event 226 expected []*Event 227 }{ 228 { 229 name: "Sorts by severity", 230 input: []*Event{ok, warn, unknown, noCheck, okOlderDiff, okOlder, critical}, 231 expected: []*Event{critical, warn, unknown, ok, okOlder, okOlderDiff, noCheck}, 232 }, 233 { 234 name: "Fallback to lastOK when severity is same", 235 input: []*Event{okOlder, ok, okOlder}, 236 expected: []*Event{ok, okOlder, okOlder}, 237 }, 238 { 239 name: "Fallback to entity name when severity is same", 240 input: []*Event{okOlderDiff, okOlder, ok, okOlder}, 241 expected: []*Event{ok, okOlder, okOlder, okOlderDiff}, 242 }, 243 { 244 name: "Events w/o a check are sorted to end", 245 input: []*Event{critical, noCheck, ok}, 246 expected: []*Event{critical, ok, noCheck}, 247 }, 248 } 249 250 for _, tc := range testCases { 251 t.Run(tc.name, func(t *testing.T) { 252 sort.Sort(EventsBySeverity(tc.input)) 253 assert.EqualValues(t, tc.expected, tc.input) 254 }) 255 } 256} 257 258func TestEventsByLastOk(t *testing.T) { 259 incident := FixtureEvent("zeta", "check") 260 incident.Check.Status = 2 // crit 261 incidentNewer := FixtureEvent("zeta", "check") 262 incidentNewer.Check.Status = 2 // crit 263 incidentNewer.Check.LastOK = 1 264 ok := FixtureEvent("zeta", "check") 265 ok.Check.Status = 0 // ok 266 okNewer := FixtureEvent("zeta", "check") 267 okNewer.Check.Status = 0 // ok 268 okNewer.Check.LastOK = 1 269 okDiffEntity := FixtureEvent("abba", "check") 270 okDiffEntity.Check.Status = 0 // ok 271 okDiffCheck := FixtureEvent("abba", "0bba") 272 okDiffCheck.Check.Status = 0 // ok 273 274 testCases := []struct { 275 name string 276 input []*Event 277 expected []*Event 278 }{ 279 { 280 name: "Sorts by lastOK", 281 input: []*Event{ok, okNewer, incidentNewer, incident}, 282 expected: []*Event{incidentNewer, incident, okNewer, ok}, 283 }, 284 { 285 name: "incidents are sorted to the top", 286 input: []*Event{okNewer, incidentNewer, ok, incident}, 287 expected: []*Event{incidentNewer, incident, okNewer, ok}, 288 }, 289 { 290 name: "Fallback to entity & check name when severity is same", 291 input: []*Event{ok, okNewer, okDiffCheck, okDiffEntity}, 292 expected: []*Event{okNewer, okDiffCheck, okDiffEntity, ok}, 293 }, 294 } 295 296 for _, tc := range testCases { 297 t.Run(tc.name, func(t *testing.T) { 298 sort.Sort(EventsByLastOk(tc.input)) 299 assert.EqualValues(t, tc.expected, tc.input) 300 }) 301 } 302} 303 304func TestEventsByTimestamp(t *testing.T) { 305 old := &Event{Timestamp: 3} 306 older := &Event{Timestamp: 2} 307 oldest := &Event{Timestamp: 1} 308 okButHow := &Event{Timestamp: 0} 309 310 testCases := []struct { 311 name string 312 inEvents []*Event 313 inDir bool 314 expected []*Event 315 }{ 316 { 317 name: "Sorts ascending", 318 inDir: false, 319 inEvents: []*Event{old, okButHow, oldest, older}, 320 expected: []*Event{okButHow, oldest, older, old}, 321 }, 322 { 323 name: "Sorts descending", 324 inDir: true, 325 inEvents: []*Event{old, okButHow, oldest, older}, 326 expected: []*Event{old, older, oldest, okButHow}, 327 }, 328 } 329 330 for _, tc := range testCases { 331 t.Run(tc.name, func(t *testing.T) { 332 sort.Sort(EventsByTimestamp(tc.inEvents, tc.inDir)) 333 assert.EqualValues(t, tc.expected, tc.inEvents) 334 }) 335 } 336} 337 338func TestSilencedBy(t *testing.T) { 339 testCases := []struct { 340 name string 341 event *Event 342 entries []*Silenced 343 expectedEntries []*Silenced 344 }{ 345 { 346 name: "no entries", 347 event: FixtureEvent("foo", "check_cpu"), 348 entries: []*Silenced{}, 349 expectedEntries: []*Silenced{}, 350 }, 351 { 352 name: "not silenced", 353 event: FixtureEvent("foo", "check_cpu"), 354 entries: []*Silenced{ 355 FixtureSilenced("entity:foo:check_mem"), 356 FixtureSilenced("entity:bar:*"), 357 FixtureSilenced("foo:check_cpu"), 358 FixtureSilenced("foo:*"), 359 FixtureSilenced("*:check_mem"), 360 }, 361 expectedEntries: []*Silenced{}, 362 }, 363 { 364 name: "silenced by check", 365 event: FixtureEvent("foo", "check_cpu"), 366 entries: []*Silenced{ 367 FixtureSilenced("*:check_cpu"), 368 }, 369 expectedEntries: []*Silenced{ 370 FixtureSilenced("*:check_cpu"), 371 }, 372 }, 373 { 374 name: "silenced by entity subscription", 375 event: FixtureEvent("foo", "check_cpu"), 376 entries: []*Silenced{ 377 FixtureSilenced("entity:foo:*"), 378 }, 379 expectedEntries: []*Silenced{ 380 FixtureSilenced("entity:foo:*"), 381 }, 382 }, 383 { 384 name: "silenced by entity's check subscription", 385 event: FixtureEvent("foo", "check_cpu"), 386 entries: []*Silenced{ 387 FixtureSilenced("entity:foo:check_cpu"), 388 }, 389 expectedEntries: []*Silenced{ 390 FixtureSilenced("entity:foo:check_cpu"), 391 }, 392 }, 393 { 394 name: "silenced by check subscription", 395 event: FixtureEvent("foo", "check_cpu"), // has a linux subscription 396 entries: []*Silenced{ 397 FixtureSilenced("linux:*"), 398 }, 399 expectedEntries: []*Silenced{ 400 FixtureSilenced("linux:*"), 401 }, 402 }, 403 { 404 name: "silenced by subscription with check", 405 event: FixtureEvent("foo", "check_cpu"), // has a linux subscription 406 entries: []*Silenced{ 407 FixtureSilenced("linux:check_cpu"), 408 }, 409 expectedEntries: []*Silenced{ 410 FixtureSilenced("linux:check_cpu"), 411 }, 412 }, 413 { 414 name: "silenced by multiple entries", 415 event: FixtureEvent("foo", "check_cpu"), // has a linux subscription 416 entries: []*Silenced{ 417 FixtureSilenced("entity:foo:*"), 418 FixtureSilenced("linux:check_cpu"), 419 }, 420 expectedEntries: []*Silenced{ 421 FixtureSilenced("entity:foo:*"), 422 FixtureSilenced("linux:check_cpu"), 423 }, 424 }, 425 { 426 name: "not silenced, silenced & client don't have a common subscription", 427 event: &Event{ 428 Check: &Check{ 429 ObjectMeta: ObjectMeta{ 430 Name: "check_cpu", 431 }, 432 Subscriptions: []string{"linux", "windows"}, 433 }, 434 Entity: &Entity{ 435 ObjectMeta: ObjectMeta{ 436 Name: "foo", 437 }, 438 Subscriptions: []string{"linux"}, 439 }, 440 }, 441 entries: []*Silenced{ 442 FixtureSilenced("windows:check_cpu"), 443 }, 444 expectedEntries: []*Silenced{}, 445 }, 446 { 447 name: "silenced, silenced & client do have a common subscription", 448 event: &Event{ 449 Check: &Check{ 450 ObjectMeta: ObjectMeta{ 451 Name: "check_cpu", 452 }, 453 Subscriptions: []string{"linux", "windows"}, 454 }, 455 Entity: &Entity{ 456 ObjectMeta: ObjectMeta{ 457 Name: "foo", 458 }, 459 Subscriptions: []string{"linux"}, 460 }, 461 }, 462 entries: []*Silenced{ 463 FixtureSilenced("linux:check_cpu"), 464 }, 465 expectedEntries: []*Silenced{ 466 FixtureSilenced("linux:check_cpu"), 467 }, 468 }, 469 } 470 471 for _, tc := range testCases { 472 t.Run(tc.name, func(t *testing.T) { 473 result := tc.event.SilencedBy(tc.entries) 474 assert.EqualValues(t, tc.expectedEntries, result) 475 }) 476 } 477} 478 479func TestIsSilencedBy(t *testing.T) { 480 testCases := []struct { 481 name string 482 event *Event 483 silence *Silenced 484 expectedResult bool 485 }{ 486 { 487 name: "silence has not started", 488 event: FixtureEvent("foo", "check_cpu"), 489 silence: &Silenced{ 490 ObjectMeta: ObjectMeta{ 491 Name: "*:check_cpu", 492 }, 493 Begin: time.Now().Add(1 * time.Hour).Unix(), 494 }, 495 expectedResult: false, 496 }, 497 { 498 name: "check matches w/ wildcard subscription", 499 event: FixtureEvent("foo", "check_cpu"), 500 silence: FixtureSilenced("*:check_cpu"), 501 expectedResult: true, 502 }, 503 { 504 name: "entity subscription matches w/ wildcard check", 505 event: FixtureEvent("foo", "check_cpu"), 506 silence: FixtureSilenced("entity:foo:*"), 507 expectedResult: true, 508 }, 509 { 510 name: "entity subscription and check match", 511 event: FixtureEvent("foo", "check_cpu"), 512 silence: FixtureSilenced("entity:foo:check_cpu"), 513 expectedResult: true, 514 }, 515 { 516 name: "subscription matches", 517 event: &Event{ 518 Check: &Check{ 519 ObjectMeta: ObjectMeta{ 520 Name: "check_cpu", 521 }, 522 Subscriptions: []string{"unix"}, 523 }, 524 Entity: &Entity{ 525 ObjectMeta: ObjectMeta{ 526 Name: "foo", 527 }, 528 Subscriptions: []string{"unix"}, 529 }, 530 }, 531 silence: FixtureSilenced("unix:check_cpu"), 532 expectedResult: true, 533 }, 534 { 535 name: "subscription does not match", 536 event: &Event{ 537 Check: &Check{ 538 ObjectMeta: ObjectMeta{ 539 Name: "check_cpu", 540 }, 541 Subscriptions: []string{"unix"}, 542 }, 543 Entity: &Entity{ 544 ObjectMeta: ObjectMeta{ 545 Name: "foo", 546 }, 547 Subscriptions: []string{"unix"}, 548 }, 549 }, 550 silence: FixtureSilenced("windows:check_cpu"), 551 expectedResult: false, 552 }, 553 { 554 name: "entity subscription doesn't match", 555 event: &Event{ 556 Check: &Check{ 557 ObjectMeta: ObjectMeta{ 558 Name: "check_cpu", 559 }, 560 Subscriptions: []string{"unix"}, 561 }, 562 Entity: &Entity{ 563 ObjectMeta: ObjectMeta{ 564 Name: "foo", 565 }, 566 Subscriptions: []string{"windows"}, 567 }, 568 }, 569 silence: FixtureSilenced("check:check_cpu"), 570 expectedResult: false, 571 }, 572 { 573 name: "check does not match", 574 event: FixtureEvent("foo", "check_mem"), 575 silence: FixtureSilenced("*:check_cpu"), 576 expectedResult: false, 577 }, 578 } 579 580 for _, tc := range testCases { 581 t.Run(tc.name, func(t *testing.T) { 582 result := tc.event.IsSilencedBy(tc.silence) 583 assert.EqualValues(t, tc.expectedResult, result) 584 }) 585 } 586} 587