engine_test.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. package alerting
  2. import (
  3. "context"
  4. "errors"
  5. "math"
  6. "net"
  7. "net/http"
  8. "net/http/httptest"
  9. "testing"
  10. "time"
  11. . "github.com/smartystreets/goconvey/convey"
  12. )
  13. type FakeEvalHandler struct {
  14. SuccessCallID int // 0 means never success
  15. CallNb int
  16. }
  17. func NewFakeEvalHandler(successCallID int) *FakeEvalHandler {
  18. return &FakeEvalHandler{
  19. SuccessCallID: successCallID,
  20. CallNb: 0,
  21. }
  22. }
  23. func (handler *FakeEvalHandler) Eval(evalContext *EvalContext) {
  24. handler.CallNb++
  25. if handler.CallNb != handler.SuccessCallID {
  26. evalContext.Error = errors.New("Fake evaluation failure")
  27. }
  28. }
  29. type FakeResultHandler struct{}
  30. func (handler *FakeResultHandler) Handle(evalContext *EvalContext) error {
  31. return nil
  32. }
  33. type FakeCommonTimeoutHandler struct {
  34. TransportTimeoutDuration time.Duration
  35. ServerBusySleepDuration time.Duration
  36. EvalSucceed bool
  37. ResultHandleSucceed bool
  38. }
  39. func NewFakeCommonTimeoutHandler(transportTimeoutDuration time.Duration, serverBusySleepDuration time.Duration) *FakeCommonTimeoutHandler {
  40. return &FakeCommonTimeoutHandler{
  41. TransportTimeoutDuration: transportTimeoutDuration,
  42. ServerBusySleepDuration: serverBusySleepDuration,
  43. EvalSucceed: false,
  44. ResultHandleSucceed: false,
  45. }
  46. }
  47. func (handler *FakeCommonTimeoutHandler) Eval(evalContext *EvalContext) {
  48. // 1. prepare mock server
  49. path := "/evaltimeout"
  50. srv := runBusyServer(path, handler.ServerBusySleepDuration)
  51. defer srv.Close()
  52. // 2. send requests
  53. url := srv.URL + path
  54. res, err := sendRequest(evalContext.Ctx, url, handler.TransportTimeoutDuration)
  55. if res != nil {
  56. defer res.Body.Close()
  57. }
  58. if err != nil {
  59. evalContext.Error = errors.New("Fake evaluation timeout test failure")
  60. return
  61. }
  62. if res.StatusCode == 200 {
  63. handler.EvalSucceed = true
  64. }
  65. evalContext.Error = errors.New("Fake evaluation timeout test failure; wrong response")
  66. }
  67. func (handler *FakeCommonTimeoutHandler) Handle(evalContext *EvalContext) error {
  68. // 1. prepare mock server
  69. path := "/resulthandle"
  70. srv := runBusyServer(path, handler.ServerBusySleepDuration)
  71. defer srv.Close()
  72. // 2. send requests
  73. url := srv.URL + path
  74. res, err := sendRequest(evalContext.Ctx, url, handler.TransportTimeoutDuration)
  75. if res != nil {
  76. defer res.Body.Close()
  77. }
  78. if err != nil {
  79. evalContext.Error = errors.New("Fake result handle timeout test failure")
  80. return evalContext.Error
  81. }
  82. if res.StatusCode == 200 {
  83. handler.ResultHandleSucceed = true
  84. return nil
  85. }
  86. evalContext.Error = errors.New("Fake result handle timeout test failure; wrong response")
  87. return evalContext.Error
  88. }
  89. func runBusyServer(path string, serverBusySleepDuration time.Duration) *httptest.Server {
  90. mux := http.NewServeMux()
  91. server := httptest.NewServer(mux)
  92. mux.HandleFunc(path, func(w http.ResponseWriter, r *http.Request) {
  93. time.Sleep(serverBusySleepDuration)
  94. })
  95. return server
  96. }
  97. func sendRequest(context context.Context, url string, transportTimeoutInterval time.Duration) (resp *http.Response, err error) {
  98. req, err := http.NewRequest("GET", url, nil)
  99. if err != nil {
  100. return nil, err
  101. }
  102. req = req.WithContext(context)
  103. transport := http.Transport{
  104. Dial: (&net.Dialer{
  105. Timeout: transportTimeoutInterval,
  106. KeepAlive: transportTimeoutInterval,
  107. }).Dial,
  108. }
  109. client := http.Client{
  110. Transport: &transport,
  111. }
  112. return client.Do(req)
  113. }
  114. func TestEngineProcessJob(t *testing.T) {
  115. Convey("Alerting engine job processing", t, func() {
  116. engine := NewEngine()
  117. engine.resultHandler = &FakeResultHandler{}
  118. job := &Job{Running: true, Rule: &Rule{}}
  119. Convey("Should trigger retry if needed", func() {
  120. Convey("error + not last attempt -> retry", func() {
  121. engine.evalHandler = NewFakeEvalHandler(0)
  122. for i := 1; i < alertMaxAttempts; i++ {
  123. attemptChan := make(chan int, 1)
  124. cancelChan := make(chan context.CancelFunc, alertMaxAttempts)
  125. engine.processJob(i, attemptChan, cancelChan, job)
  126. nextAttemptID, more := <-attemptChan
  127. So(nextAttemptID, ShouldEqual, i+1)
  128. So(more, ShouldEqual, true)
  129. So(<-cancelChan, ShouldNotBeNil)
  130. }
  131. })
  132. Convey("error + last attempt -> no retry", func() {
  133. engine.evalHandler = NewFakeEvalHandler(0)
  134. attemptChan := make(chan int, 1)
  135. cancelChan := make(chan context.CancelFunc, alertMaxAttempts)
  136. engine.processJob(alertMaxAttempts, attemptChan, cancelChan, job)
  137. nextAttemptID, more := <-attemptChan
  138. So(nextAttemptID, ShouldEqual, 0)
  139. So(more, ShouldEqual, false)
  140. So(<-cancelChan, ShouldNotBeNil)
  141. })
  142. Convey("no error -> no retry", func() {
  143. engine.evalHandler = NewFakeEvalHandler(1)
  144. attemptChan := make(chan int, 1)
  145. cancelChan := make(chan context.CancelFunc, alertMaxAttempts)
  146. engine.processJob(1, attemptChan, cancelChan, job)
  147. nextAttemptID, more := <-attemptChan
  148. So(nextAttemptID, ShouldEqual, 0)
  149. So(more, ShouldEqual, false)
  150. So(<-cancelChan, ShouldNotBeNil)
  151. })
  152. })
  153. Convey("Should trigger as many retries as needed", func() {
  154. Convey("never success -> max retries number", func() {
  155. expectedAttempts := alertMaxAttempts
  156. evalHandler := NewFakeEvalHandler(0)
  157. engine.evalHandler = evalHandler
  158. engine.processJobWithRetry(context.TODO(), job)
  159. So(evalHandler.CallNb, ShouldEqual, expectedAttempts)
  160. })
  161. Convey("always success -> never retry", func() {
  162. expectedAttempts := 1
  163. evalHandler := NewFakeEvalHandler(1)
  164. engine.evalHandler = evalHandler
  165. engine.processJobWithRetry(context.TODO(), job)
  166. So(evalHandler.CallNb, ShouldEqual, expectedAttempts)
  167. })
  168. Convey("some errors before success -> some retries", func() {
  169. expectedAttempts := int(math.Ceil(float64(alertMaxAttempts) / 2))
  170. evalHandler := NewFakeEvalHandler(expectedAttempts)
  171. engine.evalHandler = evalHandler
  172. engine.processJobWithRetry(context.TODO(), job)
  173. So(evalHandler.CallNb, ShouldEqual, expectedAttempts)
  174. })
  175. Convey("pended alert for datasource -> result handler should be worked", func() {
  176. // reduce alert timeout to test quickly
  177. originAlertTimeout := alertTimeout
  178. alertTimeout = 5 * time.Second
  179. transportTimeoutInterval := 5 * time.Second
  180. serverBusySleepDuration := 4 * time.Second
  181. evalHandler := NewFakeCommonTimeoutHandler(transportTimeoutInterval, serverBusySleepDuration)
  182. resultHandler := NewFakeCommonTimeoutHandler(transportTimeoutInterval, serverBusySleepDuration)
  183. engine.evalHandler = evalHandler
  184. engine.resultHandler = resultHandler
  185. engine.processJobWithRetry(context.TODO(), job)
  186. So(evalHandler.EvalSucceed, ShouldEqual, true)
  187. So(resultHandler.ResultHandleSucceed, ShouldEqual, true)
  188. // initialize for other tests.
  189. alertTimeout = originAlertTimeout
  190. engine.resultHandler = &FakeResultHandler{}
  191. })
  192. })
  193. })
  194. }