engine_test.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. package alerting
  2. import (
  3. "context"
  4. "errors"
  5. "math"
  6. "testing"
  7. "github.com/grafana/grafana/pkg/setting"
  8. . "github.com/smartystreets/goconvey/convey"
  9. "time"
  10. )
  11. type FakeEvalHandler struct {
  12. SuccessCallID int // 0 means never success
  13. CallNb int
  14. }
  15. func NewFakeEvalHandler(successCallID int) *FakeEvalHandler {
  16. return &FakeEvalHandler{
  17. SuccessCallID: successCallID,
  18. CallNb: 0,
  19. }
  20. }
  21. func (handler *FakeEvalHandler) Eval(evalContext *EvalContext) {
  22. handler.CallNb++
  23. if handler.CallNb != handler.SuccessCallID {
  24. evalContext.Error = errors.New("Fake evaluation failure")
  25. }
  26. }
  27. type FakeResultHandler struct{}
  28. func (handler *FakeResultHandler) Handle(evalContext *EvalContext) error {
  29. return nil
  30. }
  31. func TestEngineProcessJob(t *testing.T) {
  32. Convey("Alerting engine job processing", t, func() {
  33. engine := NewEngine()
  34. setting.AlertingEvaluationTimeout = 30 * time.Second
  35. setting.AlertingNotificationTimeout = 30 * time.Second
  36. setting.AlertingMaxAttempts = 3
  37. engine.resultHandler = &FakeResultHandler{}
  38. job := &Job{Running: true, Rule: &Rule{}}
  39. Convey("Should trigger retry if needed", func() {
  40. Convey("error + not last attempt -> retry", func() {
  41. engine.evalHandler = NewFakeEvalHandler(0)
  42. for i := 1; i < setting.AlertingMaxAttempts; i++ {
  43. attemptChan := make(chan int, 1)
  44. cancelChan := make(chan context.CancelFunc, setting.AlertingMaxAttempts)
  45. engine.processJob(i, attemptChan, cancelChan, job)
  46. nextAttemptID, more := <-attemptChan
  47. So(nextAttemptID, ShouldEqual, i+1)
  48. So(more, ShouldEqual, true)
  49. So(<-cancelChan, ShouldNotBeNil)
  50. }
  51. })
  52. Convey("error + last attempt -> no retry", func() {
  53. engine.evalHandler = NewFakeEvalHandler(0)
  54. attemptChan := make(chan int, 1)
  55. cancelChan := make(chan context.CancelFunc, setting.AlertingMaxAttempts)
  56. engine.processJob(setting.AlertingMaxAttempts, attemptChan, cancelChan, job)
  57. nextAttemptID, more := <-attemptChan
  58. So(nextAttemptID, ShouldEqual, 0)
  59. So(more, ShouldEqual, false)
  60. So(<-cancelChan, ShouldNotBeNil)
  61. })
  62. Convey("no error -> no retry", func() {
  63. engine.evalHandler = NewFakeEvalHandler(1)
  64. attemptChan := make(chan int, 1)
  65. cancelChan := make(chan context.CancelFunc, setting.AlertingMaxAttempts)
  66. engine.processJob(1, attemptChan, cancelChan, job)
  67. nextAttemptID, more := <-attemptChan
  68. So(nextAttemptID, ShouldEqual, 0)
  69. So(more, ShouldEqual, false)
  70. So(<-cancelChan, ShouldNotBeNil)
  71. })
  72. })
  73. Convey("Should trigger as many retries as needed", func() {
  74. Convey("never success -> max retries number", func() {
  75. expectedAttempts := setting.AlertingMaxAttempts
  76. evalHandler := NewFakeEvalHandler(0)
  77. engine.evalHandler = evalHandler
  78. engine.processJobWithRetry(context.TODO(), job)
  79. So(evalHandler.CallNb, ShouldEqual, expectedAttempts)
  80. })
  81. Convey("always success -> never retry", func() {
  82. expectedAttempts := 1
  83. evalHandler := NewFakeEvalHandler(1)
  84. engine.evalHandler = evalHandler
  85. engine.processJobWithRetry(context.TODO(), job)
  86. So(evalHandler.CallNb, ShouldEqual, expectedAttempts)
  87. })
  88. Convey("some errors before success -> some retries", func() {
  89. expectedAttempts := int(math.Ceil(float64(setting.AlertingMaxAttempts) / 2))
  90. evalHandler := NewFakeEvalHandler(expectedAttempts)
  91. engine.evalHandler = evalHandler
  92. engine.processJobWithRetry(context.TODO(), job)
  93. So(evalHandler.CallNb, ShouldEqual, expectedAttempts)
  94. })
  95. })
  96. })
  97. }