engine_test.go 3.2 KB

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