cloudflare/pint

Public

mirrored from https://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.31.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

internal/checks/alerts_count_test.go

385lines · modecode

1package checks_test
2
3import (
4 "fmt"
5 "testing"
6 "time"
7
8 "github.com/prometheus/common/model"
9
10 "github.com/cloudflare/pint/internal/checks"
11 "github.com/cloudflare/pint/internal/promapi"
12)
13
14func newAlertsCheck(prom *promapi.FailoverGroup) checks.RuleChecker {
15 return checks.NewAlertsCheck(prom, time.Hour*24, time.Minute, time.Minute*5)
16}
17
18func alertsText(name, uri string, count int, since string) string {
19 return fmt.Sprintf(`prometheus %q at %s would trigger %d alert(s) in the last %s`, name, uri, count, since)
20}
21
22func TestAlertsCountCheck(t *testing.T) {
23 content := "- alert: Foo Is Down\n expr: up{job=\"foo\"} == 0\n"
24
25 testCases := []checkTest{
26 {
27 description: "ignores recording rules",
28 content: "- record: foo\n expr: up == 0\n",
29 checker: newAlertsCheck,
30 prometheus: newSimpleProm,
31 problems: noProblems,
32 },
33 {
34 description: "ignores rules with syntax errors",
35 content: "- alert: Foo Is Down\n expr: sum(\n",
36 checker: newAlertsCheck,
37 prometheus: newSimpleProm,
38 problems: noProblems,
39 },
40 {
41 description: "bad request",
42 content: content,
43 checker: newAlertsCheck,
44 prometheus: newSimpleProm,
45 problems: func(uri string) []checks.Problem {
46 return []checks.Problem{
47 {
48 Fragment: `up{job="foo"} == 0`,
49 Lines: []int{2},
50 Reporter: "alerts/count",
51 Text: checkErrorBadData("prom", uri, "bad_data: bad input data"),
52 Severity: checks.Bug,
53 },
54 }
55 },
56 mocks: []*prometheusMock{
57 {
58 conds: []requestCondition{
59 requireRangeQueryPath,
60 formCond{key: "query", value: `up{job="foo"} == 0`},
61 },
62 resp: respondWithBadData(),
63 },
64 },
65 },
66 {
67 description: "connection refused / upstream not required / warning",
68 content: content,
69 checker: newAlertsCheck,
70 prometheus: func(s string) *promapi.FailoverGroup {
71 return simpleProm("prom", "http://127.0.0.1:1111", time.Second*5, false)
72 },
73 problems: func(uri string) []checks.Problem {
74 return []checks.Problem{
75 {
76 Fragment: `up{job="foo"} == 0`,
77 Lines: []int{2},
78 Reporter: "alerts/count",
79 Text: checkErrorUnableToRun(checks.AlertsCheckName, "prom", "http://127.0.0.1:1111", `connection refused`),
80 Severity: checks.Warning,
81 },
82 }
83 },
84 },
85 {
86 description: "empty response",
87 content: content,
88 checker: newAlertsCheck,
89 prometheus: newSimpleProm,
90 problems: func(uri string) []checks.Problem {
91 return []checks.Problem{
92 {
93 Fragment: `up{job="foo"} == 0`,
94 Lines: []int{2},
95 Reporter: "alerts/count",
96 Text: alertsText("prom", uri, 0, "1d"),
97 Severity: checks.Information,
98 },
99 }
100 },
101 mocks: []*prometheusMock{
102 {
103 conds: []requestCondition{
104 requireRangeQueryPath,
105 formCond{key: "query", value: `up{job="foo"} == 0`},
106 },
107 resp: respondWithEmptyMatrix(),
108 },
109 },
110 },
111 {
112 description: "multiple alerts",
113 content: content,
114 checker: newAlertsCheck,
115 prometheus: newSimpleProm,
116 problems: func(uri string) []checks.Problem {
117 return []checks.Problem{
118 {
119 Fragment: `up{job="foo"} == 0`,
120 Lines: []int{2},
121 Reporter: "alerts/count",
122 Text: alertsText("prom", uri, 7, "1d"),
123 Severity: checks.Information,
124 },
125 }
126 },
127 mocks: []*prometheusMock{
128 {
129 conds: []requestCondition{
130 requireRangeQueryPath,
131 formCond{key: "query", value: `up{job="foo"} == 0`},
132 },
133 resp: matrixResponse{
134 samples: []*model.SampleStream{
135 // 7m
136 generateSampleStream(
137 map[string]string{"job": "foo"},
138 time.Now().Add(time.Hour*-24),
139 time.Now().Add(time.Hour*-24).Add(time.Minute*6),
140 time.Minute,
141 ),
142 // 7m
143 generateSampleStream(
144 map[string]string{"job": "foo"},
145 time.Now().Add(time.Hour*-23),
146 time.Now().Add(time.Hour*-23).Add(time.Minute*6),
147 time.Minute,
148 ),
149 // 2m
150 generateSampleStream(
151 map[string]string{"job": "foo"},
152 time.Now().Add(time.Hour*-22),
153 time.Now().Add(time.Hour*-22).Add(time.Minute),
154 time.Minute,
155 ),
156 // 17m
157 generateSampleStream(
158 map[string]string{"job": "foo"},
159 time.Now().Add(time.Hour*-21),
160 time.Now().Add(time.Hour*-21).Add(time.Minute*16),
161 time.Minute,
162 ),
163 // 37m
164 generateSampleStream(
165 map[string]string{"job": "foo"},
166 time.Now().Add(time.Hour*-20),
167 time.Now().Add(time.Hour*-20).Add(time.Minute*36),
168 time.Minute,
169 ),
170 // 37m
171 generateSampleStream(
172 map[string]string{"job": "foo"},
173 time.Now().Add(time.Hour*-19),
174 time.Now().Add(time.Hour*-19).Add(time.Minute*36),
175 time.Minute,
176 ),
177 // 2h1m
178 generateSampleStream(
179 map[string]string{"job": "foo"},
180 time.Now().Add(time.Hour*-10),
181 time.Now().Add(time.Hour*-10).Add(time.Hour*2),
182 time.Minute,
183 ),
184 },
185 },
186 },
187 {
188 conds: []requestCondition{
189 requireRangeQueryPath,
190 formCond{key: "query", value: `count(up)`},
191 },
192 resp: respondWithSingleRangeVector1D(),
193 },
194 },
195 },
196 {
197 description: "for: 10m",
198 content: "- alert: Foo Is Down\n for: 10m\n expr: up{job=\"foo\"} == 0\n",
199 checker: newAlertsCheck,
200 prometheus: newSimpleProm,
201 problems: func(uri string) []checks.Problem {
202 return []checks.Problem{
203 {
204 Fragment: `up{job="foo"} == 0`,
205 Lines: []int{2, 3},
206 Reporter: "alerts/count",
207 Text: alertsText("prom", uri, 2, "1d"),
208 Severity: checks.Information,
209 },
210 }
211 },
212 mocks: []*prometheusMock{
213 {
214 conds: []requestCondition{
215 requireRangeQueryPath,
216 formCond{key: "query", value: `up{job="foo"} == 0`},
217 },
218 resp: matrixResponse{
219 samples: []*model.SampleStream{
220 generateSampleStream(
221 map[string]string{"job": "foo"},
222 time.Now().Add(time.Hour*-24),
223 time.Now().Add(time.Hour*-24).Add(time.Minute*6),
224 time.Minute,
225 ),
226 generateSampleStream(
227 map[string]string{"job": "foo"},
228 time.Now().Add(time.Hour*-23),
229 time.Now().Add(time.Hour*-23).Add(time.Minute*6),
230 time.Minute,
231 ),
232 generateSampleStream(
233 map[string]string{"job": "foo"},
234 time.Now().Add(time.Hour*-22),
235 time.Now().Add(time.Hour*-22).Add(time.Minute),
236 time.Minute,
237 ),
238 generateSampleStream(
239 map[string]string{"job": "foo"},
240 time.Now().Add(time.Hour*-21),
241 time.Now().Add(time.Hour*-21).Add(time.Minute*16),
242 time.Minute,
243 ),
244 generateSampleStream(
245 map[string]string{"job": "foo"},
246 time.Now().Add(time.Hour*-20),
247 time.Now().Add(time.Hour*-20).Add(time.Minute*9).Add(time.Second*59),
248 time.Minute,
249 ),
250 generateSampleStream(
251 map[string]string{"job": "foo"},
252 time.Now().Add(time.Hour*-18),
253 time.Now().Add(time.Hour*-18).Add(time.Hour*2),
254 time.Minute,
255 ),
256 },
257 },
258 },
259 {
260 conds: []requestCondition{
261 requireRangeQueryPath,
262 formCond{key: "query", value: `count(up)`},
263 },
264 resp: respondWithSingleRangeVector1D(),
265 },
266 },
267 },
268 {
269 description: "{__name__=}",
270 content: `
271- alert: foo
272 expr: '{__name__="up", job="foo"} == 0'
273`,
274 checker: newAlertsCheck,
275 prometheus: newSimpleProm,
276 problems: func(uri string) []checks.Problem {
277 return []checks.Problem{
278 {
279 Fragment: `{__name__="up", job="foo"} == 0`,
280 Lines: []int{3},
281 Reporter: "alerts/count",
282 Text: alertsText("prom", uri, 3, "1d"),
283 Severity: checks.Information,
284 },
285 }
286 },
287 mocks: []*prometheusMock{
288 {
289 conds: []requestCondition{
290 requireRangeQueryPath,
291 formCond{key: "query", value: `{__name__="up", job="foo"} == 0`},
292 },
293 resp: matrixResponse{
294 samples: []*model.SampleStream{
295 generateSampleStream(
296 map[string]string{"job": "foo"},
297 time.Now().Add(time.Hour*-23),
298 time.Now().Add(time.Hour*-23).Add(time.Minute*6),
299 time.Minute,
300 ),
301 generateSampleStream(
302 map[string]string{"job": "foo"},
303 time.Now().Add(time.Hour*-22),
304 time.Now().Add(time.Hour*-22).Add(time.Minute*6),
305 time.Minute,
306 ),
307 generateSampleStream(
308 map[string]string{"job": "foo"},
309 time.Now().Add(time.Hour*-21),
310 time.Now().Add(time.Hour*-21).Add(time.Minute),
311 time.Minute,
312 ),
313 },
314 },
315 },
316 {
317 conds: []requestCondition{
318 requireRangeQueryPath,
319 formCond{key: "query", value: `count(up)`},
320 },
321 resp: respondWithSingleRangeVector1D(),
322 },
323 },
324 },
325 {
326 description: "{__name__=~}",
327 content: `
328- alert: foo
329 expr: '{__name__=~"(up|foo)", job="foo"} == 0'
330`,
331 checker: newAlertsCheck,
332 prometheus: newSimpleProm,
333 problems: func(uri string) []checks.Problem {
334 return []checks.Problem{
335 {
336 Fragment: `{__name__=~"(up|foo)", job="foo"} == 0`,
337 Lines: []int{3},
338 Reporter: "alerts/count",
339 Text: alertsText("prom", uri, 3, "1d"),
340 Severity: checks.Information,
341 },
342 }
343 },
344 mocks: []*prometheusMock{
345 {
346 conds: []requestCondition{
347 requireRangeQueryPath,
348 formCond{key: "query", value: `{__name__=~"(up|foo)", job="foo"} == 0`},
349 },
350 resp: matrixResponse{
351 samples: []*model.SampleStream{
352 generateSampleStream(
353 map[string]string{"job": "foo"},
354 time.Now().Add(time.Hour*-21),
355 time.Now().Add(time.Hour*-21).Add(time.Minute*16),
356 time.Minute,
357 ),
358 generateSampleStream(
359 map[string]string{"job": "foo"},
360 time.Now().Add(time.Hour*-20),
361 time.Now().Add(time.Hour*-20).Add(time.Minute*9).Add(time.Second*59),
362 time.Minute,
363 ),
364 generateSampleStream(
365 map[string]string{"job": "foo"},
366 time.Now().Add(time.Hour*-10),
367 time.Now().Add(time.Hour*-10).Add(time.Hour*2),
368 time.Minute,
369 ),
370 },
371 },
372 },
373 {
374 conds: []requestCondition{
375 requireRangeQueryPath,
376 formCond{key: "query", value: `count(up)`},
377 },
378 resp: respondWithSingleRangeVector1D(),
379 },
380 },
381 },
382 }
383
384 runTests(t, testCases)
385}
386