1package alerting 2 3import ( 4 "context" 5 "testing" 6 7 "github.com/grafana/grafana/pkg/services/validations" 8 "github.com/grafana/grafana/pkg/tsdb/legacydata" 9 10 "github.com/stretchr/testify/require" 11) 12 13type conditionStub struct { 14 firing bool 15 operator string 16 matches []*EvalMatch 17 noData bool 18} 19 20func (c *conditionStub) Eval(context *EvalContext, reqHandler legacydata.RequestHandler) (*ConditionResult, error) { 21 return &ConditionResult{Firing: c.firing, EvalMatches: c.matches, Operator: c.operator, NoDataFound: c.noData}, nil 22} 23 24func TestAlertingEvaluationHandler(t *testing.T) { 25 handler := NewEvalHandler(nil) 26 27 t.Run("Show return triggered with single passing condition", func(t *testing.T) { 28 context := NewEvalContext(context.TODO(), &Rule{ 29 Conditions: []Condition{&conditionStub{ 30 firing: true, 31 }}, 32 }, &validations.OSSPluginRequestValidator{}) 33 34 handler.Eval(context) 35 require.Equal(t, true, context.Firing) 36 require.Equal(t, "true = true", context.ConditionEvals) 37 }) 38 39 t.Run("Show return triggered with single passing condition2", func(t *testing.T) { 40 context := NewEvalContext(context.TODO(), &Rule{ 41 Conditions: []Condition{&conditionStub{firing: true, operator: "and"}}, 42 }, &validations.OSSPluginRequestValidator{}) 43 44 handler.Eval(context) 45 require.Equal(t, true, context.Firing) 46 require.Equal(t, "true = true", context.ConditionEvals) 47 }) 48 49 t.Run("Show return false with not passing asdf", func(t *testing.T) { 50 context := NewEvalContext(context.TODO(), &Rule{ 51 Conditions: []Condition{ 52 &conditionStub{firing: true, operator: "and", matches: []*EvalMatch{{}, {}}}, 53 &conditionStub{firing: false, operator: "and"}, 54 }, 55 }, &validations.OSSPluginRequestValidator{}) 56 57 handler.Eval(context) 58 require.Equal(t, false, context.Firing) 59 require.Equal(t, "[true AND false] = false", context.ConditionEvals) 60 }) 61 62 t.Run("Show return true if any of the condition is passing with OR operator", func(t *testing.T) { 63 context := NewEvalContext(context.TODO(), &Rule{ 64 Conditions: []Condition{ 65 &conditionStub{firing: true, operator: "and"}, 66 &conditionStub{firing: false, operator: "or"}, 67 }, 68 }, &validations.OSSPluginRequestValidator{}) 69 70 handler.Eval(context) 71 require.Equal(t, true, context.Firing) 72 require.Equal(t, "[true OR false] = true", context.ConditionEvals) 73 }) 74 75 t.Run("Show return false if any of the condition is failing with AND operator", func(t *testing.T) { 76 context := NewEvalContext(context.TODO(), &Rule{ 77 Conditions: []Condition{ 78 &conditionStub{firing: true, operator: "and"}, 79 &conditionStub{firing: false, operator: "and"}, 80 }, 81 }, &validations.OSSPluginRequestValidator{}) 82 83 handler.Eval(context) 84 require.Equal(t, false, context.Firing) 85 require.Equal(t, "[true AND false] = false", context.ConditionEvals) 86 }) 87 88 t.Run("Show return true if one condition is failing with nested OR operator", func(t *testing.T) { 89 context := NewEvalContext(context.TODO(), &Rule{ 90 Conditions: []Condition{ 91 &conditionStub{firing: true, operator: "and"}, 92 &conditionStub{firing: true, operator: "and"}, 93 &conditionStub{firing: false, operator: "or"}, 94 }, 95 }, &validations.OSSPluginRequestValidator{}) 96 97 handler.Eval(context) 98 require.Equal(t, true, context.Firing) 99 require.Equal(t, "[[true AND true] OR false] = true", context.ConditionEvals) 100 }) 101 102 t.Run("Show return false if one condition is passing with nested OR operator", func(t *testing.T) { 103 context := NewEvalContext(context.TODO(), &Rule{ 104 Conditions: []Condition{ 105 &conditionStub{firing: true, operator: "and"}, 106 &conditionStub{firing: false, operator: "and"}, 107 &conditionStub{firing: false, operator: "or"}, 108 }, 109 }, &validations.OSSPluginRequestValidator{}) 110 111 handler.Eval(context) 112 require.Equal(t, false, context.Firing) 113 require.Equal(t, "[[true AND false] OR false] = false", context.ConditionEvals) 114 }) 115 116 t.Run("Show return false if a condition is failing with nested AND operator", func(t *testing.T) { 117 context := NewEvalContext(context.TODO(), &Rule{ 118 Conditions: []Condition{ 119 &conditionStub{firing: true, operator: "and"}, 120 &conditionStub{firing: false, operator: "and"}, 121 &conditionStub{firing: true, operator: "and"}, 122 }, 123 }, &validations.OSSPluginRequestValidator{}) 124 125 handler.Eval(context) 126 require.Equal(t, false, context.Firing) 127 require.Equal(t, "[[true AND false] AND true] = false", context.ConditionEvals) 128 }) 129 130 t.Run("Show return true if a condition is passing with nested OR operator", func(t *testing.T) { 131 context := NewEvalContext(context.TODO(), &Rule{ 132 Conditions: []Condition{ 133 &conditionStub{firing: true, operator: "and"}, 134 &conditionStub{firing: false, operator: "or"}, 135 &conditionStub{firing: true, operator: "or"}, 136 }, 137 }, &validations.OSSPluginRequestValidator{}) 138 139 handler.Eval(context) 140 require.Equal(t, true, context.Firing) 141 require.Equal(t, "[[true OR false] OR true] = true", context.ConditionEvals) 142 }) 143 144 t.Run("Should return false if no condition is firing using OR operator", func(t *testing.T) { 145 context := NewEvalContext(context.TODO(), &Rule{ 146 Conditions: []Condition{ 147 &conditionStub{firing: false, operator: "or"}, 148 &conditionStub{firing: false, operator: "or"}, 149 &conditionStub{firing: false, operator: "or"}, 150 }, 151 }, &validations.OSSPluginRequestValidator{}) 152 153 handler.Eval(context) 154 require.Equal(t, false, context.Firing) 155 require.Equal(t, "[[false OR false] OR false] = false", context.ConditionEvals) 156 }) 157 158 // FIXME: What should the actual test case name be here? 159 t.Run("Should not return NoDataFound if all conditions have data and using OR", func(t *testing.T) { 160 context := NewEvalContext(context.TODO(), &Rule{ 161 Conditions: []Condition{ 162 &conditionStub{operator: "or", noData: false}, 163 &conditionStub{operator: "or", noData: false}, 164 &conditionStub{operator: "or", noData: false}, 165 }, 166 }, &validations.OSSPluginRequestValidator{}) 167 168 handler.Eval(context) 169 require.False(t, context.NoDataFound) 170 }) 171 172 t.Run("Should return NoDataFound if one condition has no data", func(t *testing.T) { 173 context := NewEvalContext(context.TODO(), &Rule{ 174 Conditions: []Condition{ 175 &conditionStub{operator: "and", noData: true}, 176 }, 177 }, &validations.OSSPluginRequestValidator{}) 178 179 handler.Eval(context) 180 require.Equal(t, false, context.Firing) 181 require.True(t, context.NoDataFound) 182 }) 183 184 t.Run("Should return no data if at least one condition has no data and using AND", func(t *testing.T) { 185 context := NewEvalContext(context.TODO(), &Rule{ 186 Conditions: []Condition{ 187 &conditionStub{operator: "and", noData: true}, 188 &conditionStub{operator: "and", noData: false}, 189 }, 190 }, &validations.OSSPluginRequestValidator{}) 191 192 handler.Eval(context) 193 require.True(t, context.NoDataFound) 194 }) 195 196 t.Run("Should return no data if at least one condition has no data and using OR", func(t *testing.T) { 197 context := NewEvalContext(context.TODO(), &Rule{ 198 Conditions: []Condition{ 199 &conditionStub{operator: "or", noData: true}, 200 &conditionStub{operator: "or", noData: false}, 201 }, 202 }, &validations.OSSPluginRequestValidator{}) 203 204 handler.Eval(context) 205 require.True(t, context.NoDataFound) 206 }) 207} 208