query_test.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. package conditions
  2. import (
  3. "testing"
  4. null "gopkg.in/guregu/null.v3"
  5. "github.com/grafana/grafana/pkg/bus"
  6. "github.com/grafana/grafana/pkg/components/simplejson"
  7. m "github.com/grafana/grafana/pkg/models"
  8. "github.com/grafana/grafana/pkg/services/alerting"
  9. "github.com/grafana/grafana/pkg/tsdb"
  10. . "github.com/smartystreets/goconvey/convey"
  11. )
  12. func TestQueryCondition(t *testing.T) {
  13. Convey("when evaluating query condition", t, func() {
  14. queryConditionScenario("Given avg() and > 100", func(ctx *queryConditionTestContext) {
  15. ctx.reducer = `{"type": "avg"}`
  16. ctx.evaluator = `{"type": "gt", "params": [100]}`
  17. Convey("Can read query condition from json model", func() {
  18. ctx.exec()
  19. So(ctx.condition.Query.From, ShouldEqual, "5m")
  20. So(ctx.condition.Query.To, ShouldEqual, "now")
  21. So(ctx.condition.Query.DatasourceId, ShouldEqual, 1)
  22. Convey("Can read query reducer", func() {
  23. reducer, ok := ctx.condition.Reducer.(*SimpleReducer)
  24. So(ok, ShouldBeTrue)
  25. So(reducer.Type, ShouldEqual, "avg")
  26. })
  27. Convey("Can read evaluator", func() {
  28. evaluator, ok := ctx.condition.Evaluator.(*ThresholdEvaluator)
  29. So(ok, ShouldBeTrue)
  30. So(evaluator.Type, ShouldEqual, "gt")
  31. })
  32. })
  33. Convey("should fire when avg is above 100", func() {
  34. points := tsdb.NewTimeSeriesPointsFromArgs(120, 0)
  35. ctx.series = tsdb.TimeSeriesSlice{tsdb.NewTimeSeries("test1", points)}
  36. ctx.exec()
  37. So(ctx.result.Error, ShouldBeNil)
  38. So(ctx.result.Firing, ShouldBeTrue)
  39. })
  40. Convey("Should not fire when avg is below 100", func() {
  41. points := tsdb.NewTimeSeriesPointsFromArgs(90, 0)
  42. ctx.series = tsdb.TimeSeriesSlice{tsdb.NewTimeSeries("test1", points)}
  43. ctx.exec()
  44. So(ctx.result.Error, ShouldBeNil)
  45. So(ctx.result.Firing, ShouldBeFalse)
  46. })
  47. Convey("Should fire if only first serie matches", func() {
  48. ctx.series = tsdb.TimeSeriesSlice{
  49. tsdb.NewTimeSeries("test1", tsdb.NewTimeSeriesPointsFromArgs(120, 0)),
  50. tsdb.NewTimeSeries("test2", tsdb.NewTimeSeriesPointsFromArgs(0, 0)),
  51. }
  52. ctx.exec()
  53. So(ctx.result.Error, ShouldBeNil)
  54. So(ctx.result.Firing, ShouldBeTrue)
  55. })
  56. Convey("Empty series", func() {
  57. Convey("Should set NoDataFound both series are empty", func() {
  58. ctx.series = tsdb.TimeSeriesSlice{
  59. tsdb.NewTimeSeries("test1", tsdb.NewTimeSeriesPointsFromArgs()),
  60. tsdb.NewTimeSeries("test2", tsdb.NewTimeSeriesPointsFromArgs()),
  61. }
  62. ctx.exec()
  63. So(ctx.result.Error, ShouldBeNil)
  64. So(ctx.result.NoDataFound, ShouldBeTrue)
  65. })
  66. Convey("Should set NoDataFound both series contains null", func() {
  67. ctx.series = tsdb.TimeSeriesSlice{
  68. tsdb.NewTimeSeries("test1", tsdb.TimeSeriesPoints{tsdb.TimePoint{null.FloatFromPtr(nil), null.FloatFrom(0)}}),
  69. tsdb.NewTimeSeries("test2", tsdb.TimeSeriesPoints{tsdb.TimePoint{null.FloatFromPtr(nil), null.FloatFrom(0)}}),
  70. }
  71. ctx.exec()
  72. So(ctx.result.Error, ShouldBeNil)
  73. So(ctx.result.NoDataFound, ShouldBeTrue)
  74. })
  75. Convey("Should not set NoDataFound if one serie is empty", func() {
  76. ctx.series = tsdb.TimeSeriesSlice{
  77. tsdb.NewTimeSeries("test1", tsdb.NewTimeSeriesPointsFromArgs()),
  78. tsdb.NewTimeSeries("test2", tsdb.NewTimeSeriesPointsFromArgs(120, 0)),
  79. }
  80. ctx.exec()
  81. So(ctx.result.Error, ShouldBeNil)
  82. So(ctx.result.NoDataFound, ShouldBeFalse)
  83. })
  84. })
  85. })
  86. })
  87. }
  88. type queryConditionTestContext struct {
  89. reducer string
  90. evaluator string
  91. series tsdb.TimeSeriesSlice
  92. result *alerting.EvalContext
  93. condition *QueryCondition
  94. }
  95. type queryConditionScenarioFunc func(c *queryConditionTestContext)
  96. func (ctx *queryConditionTestContext) exec() {
  97. jsonModel, err := simplejson.NewJson([]byte(`{
  98. "type": "query",
  99. "query": {
  100. "params": ["A", "5m", "now"],
  101. "datasourceId": 1,
  102. "model": {"target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}
  103. },
  104. "reducer":` + ctx.reducer + `,
  105. "evaluator":` + ctx.evaluator + `
  106. }`))
  107. So(err, ShouldBeNil)
  108. condition, err := NewQueryCondition(jsonModel, 0)
  109. So(err, ShouldBeNil)
  110. ctx.condition = condition
  111. condition.HandleRequest = func(req *tsdb.Request) (*tsdb.Response, error) {
  112. return &tsdb.Response{
  113. Results: map[string]*tsdb.QueryResult{
  114. "A": {Series: ctx.series},
  115. },
  116. }, nil
  117. }
  118. condition.Eval(ctx.result)
  119. }
  120. func queryConditionScenario(desc string, fn queryConditionScenarioFunc) {
  121. Convey(desc, func() {
  122. bus.AddHandler("test", func(query *m.GetDataSourceByIdQuery) error {
  123. query.Result = &m.DataSource{Id: 1, Type: "graphite"}
  124. return nil
  125. })
  126. ctx := &queryConditionTestContext{}
  127. ctx.result = &alerting.EvalContext{
  128. Rule: &alerting.Rule{},
  129. }
  130. fn(ctx)
  131. })
  132. }