alert_test.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. package alert
  2. import (
  3. "strings"
  4. "testing"
  5. )
  6. func goodAlert() *Alert {
  7. return &Alert{
  8. CompanyID: "acme-001",
  9. SourceID: "prom-prod",
  10. Severity: SeverityCritical,
  11. Category: "storage",
  12. Title: "Disk full on db-prod-03",
  13. Body: "92% used",
  14. Data: map[string]string{"host": "db-prod-03", "used_pct": "92"},
  15. DedupeKey: "disk:db-prod-03:full",
  16. }
  17. }
  18. func TestValidate_OK(t *testing.T) {
  19. if err := goodAlert().Validate(); err != nil {
  20. t.Fatalf("expected nil, got %v", err)
  21. }
  22. }
  23. func TestValidate_BadCompanyID(t *testing.T) {
  24. a := goodAlert()
  25. a.CompanyID = "ACME-001" // uppercase not allowed
  26. if err := a.Validate(); err == nil {
  27. t.Fatal("expected error for uppercase company_id")
  28. }
  29. }
  30. func TestValidate_UnknownSeverity(t *testing.T) {
  31. a := goodAlert()
  32. a.Severity = "emergency"
  33. err := a.Validate()
  34. if err == nil {
  35. t.Fatal("expected error for unknown severity")
  36. }
  37. if !IsInvalid(err) {
  38. t.Fatalf("expected *ErrInvalid, got %T", err)
  39. }
  40. if !strings.Contains(err.Error(), "severity") {
  41. t.Fatalf("expected severity reason, got %v", err)
  42. }
  43. }
  44. func TestValidate_DedupeKeyTooLong(t *testing.T) {
  45. a := goodAlert()
  46. a.DedupeKey = strings.Repeat("a", 129)
  47. if err := a.Validate(); err == nil {
  48. t.Fatal("expected error for long dedupe_key")
  49. }
  50. }
  51. func TestValidate_DataKeyTooLong(t *testing.T) {
  52. a := goodAlert()
  53. a.Data = map[string]string{strings.Repeat("k", 65): "v"}
  54. if err := a.Validate(); err == nil {
  55. t.Fatal("expected error for long data key")
  56. }
  57. }
  58. func TestValidate_TooManyDataKeys(t *testing.T) {
  59. a := goodAlert()
  60. a.Data = make(map[string]string, MaxDataKeys+1)
  61. for i := 0; i < MaxDataKeys+1; i++ {
  62. a.Data[shortKey(i)] = "v"
  63. }
  64. if err := a.Validate(); err == nil {
  65. t.Fatal("expected error for too many data keys")
  66. }
  67. }
  68. func TestInminentColapseBypass(t *testing.T) {
  69. if !SeverityInminentColapse.InminentColapseBypass() {
  70. t.Fatal("inminent_colapse must bypass quiet hours")
  71. }
  72. if SeverityCritical.InminentColapseBypass() {
  73. t.Fatal("critical must NOT bypass quiet hours")
  74. }
  75. }
  76. func TestNewID_UniqueAndSortable(t *testing.T) {
  77. a := NewID()
  78. b := NewID()
  79. if a == b {
  80. t.Fatalf("ids collided: %s", a)
  81. }
  82. if len(a) != 26 {
  83. t.Fatalf("id length expected 26, got %d (%q)", len(a), a)
  84. }
  85. }
  86. func shortKey(i int) string {
  87. const alphabet = "abcdefghijklmnopqrstuvwxyz"
  88. if i < len(alphabet) {
  89. return string(alphabet[i])
  90. }
  91. return shortKey(i/len(alphabet)) + string(alphabet[i%len(alphabet)])
  92. }
  93. func TestSeverityRank(t *testing.T) {
  94. cases := []struct {
  95. sev Severity
  96. want int
  97. }{
  98. {SeverityInfo, 0},
  99. {SeverityWarning, 1},
  100. {SeverityCritical, 2},
  101. {SeverityInminentColapse, 3},
  102. {Severity("bogus"), -1},
  103. }
  104. for _, tc := range cases {
  105. if got := tc.sev.Rank(); got != tc.want {
  106. t.Errorf("Severity(%q).Rank() = %d, want %d", tc.sev, got, tc.want)
  107. }
  108. }
  109. }
  110. func TestMinSeverityRank(t *testing.T) {
  111. if MinSeverityRank("") != 0 {
  112. t.Error("empty min_severity should default to rank 0 (info and above)")
  113. }
  114. if MinSeverityRank("critical") != 2 {
  115. t.Error("critical should be rank 2")
  116. }
  117. if MinSeverityRank("inminent_colapse") != 3 {
  118. t.Error("inminent_colapse should be rank 3")
  119. }
  120. }