cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.75.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/discovery/git_branch_test.go

1351lines · modecode

1package discovery_test
2
3import (
4 "encoding/json"
5 "errors"
6 "fmt"
7 "os"
8 "regexp"
9 "strings"
10 "testing"
11
12 "github.com/google/go-cmp/cmp"
13 "github.com/prometheus/common/model"
14 "github.com/stretchr/testify/require"
15
16 "github.com/cloudflare/pint/internal/diags"
17 "github.com/cloudflare/pint/internal/discovery"
18 "github.com/cloudflare/pint/internal/git"
19 "github.com/cloudflare/pint/internal/parser"
20)
21
22func gitCommit(t *testing.T, message string) {
23 t.Setenv("GIT_AUTHOR_NAME", "pint")
24 t.Setenv("GIT_AUTHOR_EMAIL", "pint@example.com")
25 t.Setenv("GIT_COMMITTER_NAME", "pint")
26 t.Setenv("GIT_COMMITTER_EMAIL", "pint")
27 _, err := git.RunGit("commit", "-am", "commit "+message)
28 require.NoError(t, err, "git commit %s", message)
29}
30
31func commitFile(t *testing.T, path, content, message string) {
32 err := os.WriteFile(path, []byte(content), 0o644)
33 require.NoError(t, err, "write %s", path)
34 _, err = git.RunGit("add", path)
35 require.NoError(t, err, "git add")
36 gitCommit(t, message)
37}
38
39func TestGitBranchFinder(t *testing.T) {
40 includeAll := []*regexp.Regexp{regexp.MustCompile(".*")}
41
42 mustParse := func(offset int, s string) parser.Rule {
43 p := parser.NewParser(false, parser.PrometheusSchema, model.UTF8Validation)
44 file := p.Parse(strings.NewReader(strings.Repeat("\n", offset) + s))
45 if file.Error.Err != nil {
46 panic(fmt.Sprintf("failed to parse rule:\n---\n%s\n---\nerror: %s", s, file.Error))
47 }
48 if len(file.Groups) != 1 {
49 panic(fmt.Sprintf("wrong number of groups returned: %d\n---\n%s\n---", len(file.Groups), s))
50 }
51 if len(file.Groups[0].Rules) != 1 {
52 panic(fmt.Sprintf("wrong number of rules returned: %d\n---\n%s\n---", len(file.Groups[0].Rules), s))
53 }
54 return file.Groups[0].Rules[0]
55 }
56
57 type setupFn func(t *testing.T)
58
59 type testCaseT struct {
60 setup setupFn
61 title string
62 err string
63 entries []discovery.Entry
64 finder discovery.GitBranchFinder
65 }
66
67 testCases := []testCaseT{
68 {
69 title: "git list PR commits error - main",
70 setup: func(_ *testing.T) {},
71 finder: discovery.NewGitBranchFinder(
72 func(args ...string) ([]byte, error) {
73 return nil, fmt.Errorf("mock git error: %v", args)
74 },
75 git.NewPathFilter(includeAll, nil, nil),
76 "main",
77 50,
78 parser.PrometheusSchema, model.UTF8Validation,
79 nil,
80 ),
81 entries: nil,
82 err: "failed to get the list of modified files from git: mock git error: [log --reverse --no-merges --first-parent --format=%H --name-status main..HEAD]",
83 },
84 {
85 title: "git list PR commits error - master",
86 setup: func(_ *testing.T) {},
87 finder: discovery.NewGitBranchFinder(
88 func(args ...string) ([]byte, error) {
89 return nil, fmt.Errorf("mock git error: %v", args)
90 },
91 git.NewPathFilter(includeAll, nil, nil),
92 "master",
93 50,
94 parser.PrometheusSchema, model.UTF8Validation,
95 nil,
96 ),
97 entries: nil,
98 err: "failed to get the list of modified files from git: mock git error: [log --reverse --no-merges --first-parent --format=%H --name-status master..HEAD]",
99 },
100 {
101 title: "too many commits",
102 setup: func(t *testing.T) {
103 commitFile(t, "rules.yml", "# v1\n", "v1")
104
105 _, err := git.RunGit("checkout", "-b", "v2")
106 require.NoError(t, err, "git checkout v2")
107
108 commitFile(t, "rules.yml", "# v2-1\n", "v2-1")
109 commitFile(t, "rules.yml", "# v2-2\n", "v2-2")
110 commitFile(t, "rules.yml", "# v2-3\n", "v2-3")
111 commitFile(t, "rules.yml", "# v2-4\n", "v2-4")
112 },
113 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 3, parser.PrometheusSchema, model.UTF8Validation, nil),
114 entries: nil,
115 err: "number of commits to check (4) is higher than maxCommits (3), exiting",
116 },
117 {
118 title: "git list modified files error",
119 setup: func(_ *testing.T) {},
120 finder: discovery.NewGitBranchFinder(
121 func(args ...string) ([]byte, error) {
122 switch strings.Join(args, " ") {
123 case "log --format=%H --no-abbrev-commit --reverse main..HEAD":
124 return []byte("c1\nc2\nc3\nc4\n"), nil
125 default:
126 return nil, fmt.Errorf("mock git error: %v", args)
127 }
128 },
129 git.NewPathFilter(includeAll, nil, nil),
130 "main",
131 4,
132 parser.PrometheusSchema, model.UTF8Validation,
133 nil,
134 ),
135 entries: nil,
136 err: "failed to get the list of modified files from git: mock git error: [log --reverse --no-merges --first-parent --format=%H --name-status main..HEAD]",
137 },
138 {
139 title: "git get commit message error",
140 setup: func(_ *testing.T) {},
141 finder: discovery.NewGitBranchFinder(
142 func(args ...string) ([]byte, error) {
143 switch strings.Join(args, " ") {
144 case "log --reverse --no-merges --first-parent --format=%H --name-status main..HEAD":
145 return []byte("c1\nA\trules.yml\n"), nil
146 default:
147 return nil, fmt.Errorf("mock git error: %v", args)
148 }
149 },
150 git.NewPathFilter(includeAll, nil, nil),
151 "main",
152 4,
153 parser.PrometheusSchema, model.UTF8Validation,
154 nil,
155 ),
156 entries: nil,
157 err: "failed to get commit message for c1: mock git error: [show -s --format=%B c1]",
158 },
159 {
160 title: "git blame error",
161 setup: func(_ *testing.T) {},
162 finder: discovery.NewGitBranchFinder(
163 func(args ...string) ([]byte, error) {
164 switch strings.Join(args, " ") {
165 case "log --reverse --no-merges --first-parent --format=%H --name-status main..HEAD":
166 return []byte("c1\nA\trules.yml\n"), nil
167 case "ls-tree c1^ rules.yml":
168 return []byte("100644 blob c0\trules.yml"), nil
169 case "ls-tree c1 rules.yml":
170 return []byte("100644 blob c1\trules.yml"), nil
171 case "show -s --format=%B c1":
172 return []byte(""), nil
173 default:
174 return nil, fmt.Errorf("mock git error: %v", args)
175 }
176 },
177 git.NewPathFilter(includeAll, nil, nil),
178 "main",
179 4,
180 parser.PrometheusSchema, model.UTF8Validation,
181 nil,
182 ),
183 entries: nil,
184 err: "failed to run git blame for rules.yml: mock git error: [blame --line-porcelain c1 -- rules.yml]",
185 },
186 {
187 title: "no rules in file",
188 setup: func(t *testing.T) {
189 commitFile(t, "rules.yml", "# v1\n", "v1")
190
191 _, err := git.RunGit("checkout", "-b", "v2")
192 require.NoError(t, err, "git checkout v2")
193
194 commitFile(t, "rules.yml", "# v2\n", "v2")
195 },
196 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
197 entries: nil,
198 },
199 {
200 title: "no rule changes",
201 setup: func(t *testing.T) {
202 commitFile(t, "rules.yml", `
203groups:
204- name: v1
205 rules:
206 - record: up:count
207 expr: count(up == 1)
208`, "v1")
209
210 _, err := git.RunGit("checkout", "-b", "v2")
211 require.NoError(t, err, "git checkout v2")
212
213 commitFile(t, "rules.yml", `
214groups:
215- name: v2
216 rules:
217 - record: up:count
218 expr: count(up == 1)
219`, "v2")
220 },
221 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
222 entries: []discovery.Entry{
223 {
224 State: discovery.Noop,
225 Path: discovery.Path{
226 Name: "rules.yml",
227 SymlinkTarget: "rules.yml",
228 },
229 ModifiedLines: []int{},
230 Rule: mustParse(4, " - record: up:count\n expr: count(up == 1)\n"),
231 },
232 },
233 },
234 {
235 title: "rule changed - strict",
236 setup: func(t *testing.T) {
237 commitFile(t, "rules.yml", `
238groups:
239- name: v1
240 rules:
241 - record: up:count
242 expr: count(up)
243`, "v1")
244
245 _, err := git.RunGit("checkout", "-b", "v2")
246 require.NoError(t, err, "git checkout v2")
247
248 commitFile(t, "rules.yml", `
249groups:
250- name: v2
251 rules:
252 - record: up:count
253 expr: count(up == 1)
254`, "v2")
255 },
256 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
257 entries: []discovery.Entry{
258 {
259 State: discovery.Modified,
260 Path: discovery.Path{
261 Name: "rules.yml",
262 SymlinkTarget: "rules.yml",
263 },
264 ModifiedLines: []int{6},
265 Rule: mustParse(4, " - record: up:count\n expr: count(up == 1)\n"),
266 },
267 },
268 },
269 {
270 title: "rule changed - relaxed",
271 setup: func(t *testing.T) {
272 commitFile(t, "rules.yml", `
273- record: up:count
274 expr: count(up)
275`, "v1")
276
277 _, err := git.RunGit("checkout", "-b", "v2")
278 require.NoError(t, err, "git checkout v2")
279
280 commitFile(t, "rules.yml", `
281- record: up:count
282 expr: count(up == 1)
283`, "v2")
284 },
285 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
286 entries: []discovery.Entry{
287 {
288 State: discovery.Modified,
289 Path: discovery.Path{
290 Name: "rules.yml",
291 SymlinkTarget: "rules.yml",
292 },
293 ModifiedLines: []int{3},
294 Rule: mustParse(1, "- record: up:count\n expr: count(up == 1)\n"),
295 },
296 },
297 },
298 {
299 title: "rule changed - empty include list",
300 setup: func(t *testing.T) {
301 commitFile(t, "rules.yml", `
302- record: up:count
303 expr: count(up)
304`, "v1")
305
306 _, err := git.RunGit("checkout", "-b", "v2")
307 require.NoError(t, err, "git checkout v2")
308
309 commitFile(t, "rules.yml", `
310- record: up:count
311 expr: count(up == 1)
312`, "v2")
313 },
314 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(nil, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
315 entries: []discovery.Entry{
316 {
317 State: discovery.Modified,
318 Path: discovery.Path{
319 Name: "rules.yml",
320 SymlinkTarget: "rules.yml",
321 },
322 ModifiedLines: []int{3},
323 Rule: mustParse(1, "- record: up:count\n expr: count(up == 1)\n"),
324 },
325 },
326 },
327 {
328 title: "rule changed but file excluded",
329 setup: func(t *testing.T) {
330 commitFile(t, "rules.yml", `
331groups:
332- name: v1
333 rules:
334 - record: up:count
335 expr: count(up)
336`, "v1")
337
338 _, err := git.RunGit("checkout", "-b", "v2")
339 require.NoError(t, err, "git checkout v2")
340
341 commitFile(t, "rules.yml", `
342groups:
343- name: v2
344 rules:
345 - record: up:count
346 expr: count(up == 1)
347`, "v2")
348 },
349 finder: discovery.NewGitBranchFinder(
350 git.RunGit,
351 git.NewPathFilter([]*regexp.Regexp{regexp.MustCompile("^foo#")}, nil, nil),
352 "main",
353 4,
354 parser.PrometheusSchema, model.UTF8Validation,
355 nil,
356 ),
357 entries: nil,
358 },
359 {
360 title: "rule changed - [skip ci]",
361 setup: func(t *testing.T) {
362 commitFile(t, "rules.yml", `
363groups:
364- name: v1
365 rules:
366 - record: up:count
367 expr: count(up)
368`, "v1")
369
370 _, err := git.RunGit("checkout", "-b", "v2")
371 require.NoError(t, err, "git checkout v2")
372
373 commitFile(t, "rules.yml", `
374groups:
375- name: v2
376 rules:
377 - record: up:count
378 expr: count(up == 1)
379`, "v2\nskip this commit\n[skip ci]\n")
380 },
381 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
382 entries: nil,
383 },
384 {
385 title: "rule changed - [no ci]",
386 setup: func(t *testing.T) {
387 commitFile(t, "rules.yml", `
388groups:
389- name: v1
390 rules:
391 - record: up:count
392 expr: count(up)
393`, "v1")
394
395 _, err := git.RunGit("checkout", "-b", "v2")
396 require.NoError(t, err, "git checkout v2")
397
398 commitFile(t, "rules.yml", `
399groups:
400- name: v2
401 rules:
402 - record: up:count
403 expr: count(up == 1)
404`, "v2\nskip this commit\n[no ci]\n")
405 },
406 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
407 entries: nil,
408 },
409 {
410 title: "rule symlinked",
411 setup: func(t *testing.T) {
412 commitFile(t, "rules.yml", `
413- record: up:count
414 expr: count(up)
415`, "v1")
416
417 _, err := git.RunGit("checkout", "-b", "v2")
418 require.NoError(t, err, "git checkout v2")
419
420 err = os.Symlink("rules.yml", "symlink.yml")
421 require.NoError(t, err, "symlink")
422 _, err = git.RunGit("add", "symlink.yml")
423 require.NoError(t, err, "git add")
424 gitCommit(t, "v2")
425 },
426 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
427 entries: []discovery.Entry{
428 {
429 State: discovery.Added,
430 Path: discovery.Path{
431 Name: "symlink.yml",
432 SymlinkTarget: "rules.yml",
433 },
434 ModifiedLines: []int{2, 3},
435 Rule: mustParse(1, "- record: up:count\n expr: count(up)\n"),
436 },
437 },
438 },
439 {
440 title: "rule changed - multiple rules",
441 setup: func(t *testing.T) {
442 commitFile(t, "rules.yml", `
443groups:
444- name: v1
445 rules:
446 - record: up:count:1
447 expr: count(up)
448 - record: up:count:2
449 expr: count(up)
450 - record: up:count:3
451 expr: count(up)
452`, "v1")
453
454 _, err := git.RunGit("checkout", "-b", "v2")
455 require.NoError(t, err, "git checkout v2")
456
457 commitFile(t, "rules.yml", `
458groups:
459- name: v2
460 rules:
461 - record: up:count:1
462 expr: count(up == 1)
463 - record: up:count:2a
464 expr: count(up)
465 - record: up:count:3
466 expr: count(up)
467 - record: up:count:4
468 expr: count(up)
469`, "v2")
470 },
471 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
472 entries: []discovery.Entry{
473 {
474 State: discovery.Modified,
475 Path: discovery.Path{
476 Name: "rules.yml",
477 SymlinkTarget: "rules.yml",
478 },
479 ModifiedLines: []int{6},
480 Rule: mustParse(4, " - record: up:count:1\n expr: count(up == 1)\n"),
481 },
482 {
483 State: discovery.Added,
484 Path: discovery.Path{
485 Name: "rules.yml",
486 SymlinkTarget: "rules.yml",
487 },
488 ModifiedLines: []int{7},
489 Rule: mustParse(6, " - record: up:count:2a\n expr: count(up)\n"),
490 },
491 {
492 State: discovery.Noop,
493 Path: discovery.Path{
494 Name: "rules.yml",
495 SymlinkTarget: "rules.yml",
496 },
497 ModifiedLines: []int{},
498 Rule: mustParse(8, " - record: up:count:3\n expr: count(up)\n"),
499 },
500 {
501 State: discovery.Added,
502 Path: discovery.Path{
503 Name: "rules.yml",
504 SymlinkTarget: "rules.yml",
505 },
506 ModifiedLines: []int{11, 12},
507 Rule: mustParse(10, " - record: up:count:4\n expr: count(up)\n"),
508 },
509 {
510 State: discovery.Removed,
511 Path: discovery.Path{
512 Name: "rules.yml",
513 SymlinkTarget: "rules.yml",
514 },
515 ModifiedLines: []int{7},
516 Rule: mustParse(6, " - record: up:count:2\n expr: count(up)\n"),
517 },
518 },
519 },
520 {
521 title: "rule changed - added extra line",
522 setup: func(t *testing.T) {
523 commitFile(t, "rules.yml", `
524- alert: rule1
525 expr: sum(foo) by(job)
526- alert: rule2
527 expr: sum(foo) by(job)
528 for: 0s
529`, "v1")
530
531 _, err := git.RunGit("checkout", "-b", "v2")
532 require.NoError(t, err, "git checkout v2")
533
534 commitFile(t, "rules.yml", `
535- alert: rule1
536 expr: sum(foo) by(job)
537 for: 0s
538- alert: rule2
539 expr: sum(foo) by(job)
540 for: 0s
541`, "v2")
542 },
543 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
544 entries: []discovery.Entry{
545 {
546 State: discovery.Modified,
547 Path: discovery.Path{
548 Name: "rules.yml",
549 SymlinkTarget: "rules.yml",
550 },
551 ModifiedLines: []int{4},
552 Rule: mustParse(1, "- alert: rule1\n expr: sum(foo) by(job)\n for: 0s\n"),
553 },
554 {
555 State: discovery.Noop,
556 Path: discovery.Path{
557 Name: "rules.yml",
558 SymlinkTarget: "rules.yml",
559 },
560 ModifiedLines: []int{},
561 Rule: mustParse(4, "- alert: rule2\n expr: sum(foo) by(job)\n for: 0s\n"),
562 },
563 },
564 },
565 {
566 title: "rule removed - head",
567 setup: func(t *testing.T) {
568 commitFile(t, "rules.yml", `
569- alert: rule1
570 expr: sum(foo) by(job)
571- alert: rule2
572 expr: sum(foo) by(job)
573`, "v1")
574
575 _, err := git.RunGit("checkout", "-b", "v2")
576 require.NoError(t, err, "git checkout v2")
577
578 commitFile(t, "rules.yml", `
579- alert: rule2
580 expr: sum(foo) by(job)
581`, "v2")
582 },
583 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
584 entries: []discovery.Entry{
585 {
586 State: discovery.Noop,
587 Path: discovery.Path{
588 Name: "rules.yml",
589 SymlinkTarget: "rules.yml",
590 },
591 ModifiedLines: []int{},
592 Rule: mustParse(1, "- alert: rule2\n expr: sum(foo) by(job)\n"),
593 },
594 {
595 State: discovery.Removed,
596 Path: discovery.Path{
597 Name: "rules.yml",
598 SymlinkTarget: "rules.yml",
599 },
600 ModifiedLines: []int{2, 3},
601 Rule: mustParse(1, "- alert: rule1\n expr: sum(foo) by(job)\n"),
602 },
603 },
604 },
605 {
606 title: "rule removed - tail",
607 setup: func(t *testing.T) {
608 commitFile(t, "rules.yml", `
609- alert: rule1
610 expr: sum(foo) by(job)
611- alert: rule2
612 expr: sum(foo) by(job)
613`, "v1")
614
615 _, err := git.RunGit("checkout", "-b", "v2")
616 require.NoError(t, err, "git checkout v2")
617
618 commitFile(t, "rules.yml", `
619- alert: rule1
620 expr: sum(foo) by(job)
621`, "v2")
622 },
623 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
624 entries: []discovery.Entry{
625 {
626 State: discovery.Noop,
627 Path: discovery.Path{
628 Name: "rules.yml",
629 SymlinkTarget: "rules.yml",
630 },
631 ModifiedLines: []int{},
632 Rule: mustParse(1, "- alert: rule1\n expr: sum(foo) by(job)\n"),
633 },
634 {
635 State: discovery.Removed,
636 Path: discovery.Path{
637 Name: "rules.yml",
638 SymlinkTarget: "rules.yml",
639 },
640 ModifiedLines: []int{4, 5},
641 Rule: mustParse(3, "- alert: rule2\n expr: sum(foo) by(job)\n"),
642 },
643 },
644 },
645 {
646 title: "rule removed - middle",
647 setup: func(t *testing.T) {
648 commitFile(t, "rules.yml", `
649- alert: rule1
650 expr: sum(foo) by(job)
651- alert: rule2
652 expr: sum(foo) by(job)
653- alert: rule3
654 expr: sum(foo) by(job)
655`, "v1")
656
657 _, err := git.RunGit("checkout", "-b", "v2")
658 require.NoError(t, err, "git checkout v2")
659
660 commitFile(t, "rules.yml", `
661- alert: rule1
662 expr: sum(foo) by(job)
663- alert: rule3
664 expr: sum(foo) by(job)
665`, "v2")
666 },
667 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
668 entries: []discovery.Entry{
669 {
670 State: discovery.Noop,
671 Path: discovery.Path{
672 Name: "rules.yml",
673 SymlinkTarget: "rules.yml",
674 },
675 ModifiedLines: []int{},
676 Rule: mustParse(1, "- alert: rule1\n expr: sum(foo) by(job)\n"),
677 },
678 {
679 State: discovery.Noop,
680 Path: discovery.Path{
681 Name: "rules.yml",
682 SymlinkTarget: "rules.yml",
683 },
684 ModifiedLines: []int{},
685 Rule: mustParse(3, "- alert: rule3\n expr: sum(foo) by(job)\n"),
686 },
687 {
688 State: discovery.Removed,
689 Path: discovery.Path{
690 Name: "rules.yml",
691 SymlinkTarget: "rules.yml",
692 },
693 ModifiedLines: []int{4, 5},
694 Rule: mustParse(3, "- alert: rule2\n expr: sum(foo) by(job)\n"),
695 },
696 },
697 },
698 {
699 title: "rule fixed",
700 setup: func(t *testing.T) {
701 commitFile(t, "rules.yml", `
702groups:
703- name: v1
704 rules:
705 - record: up:count
706 expr: count(up)
707 expr: sum(up)
708`, "v1")
709
710 _, err := git.RunGit("checkout", "-b", "v2")
711 require.NoError(t, err, "git checkout v2")
712
713 commitFile(t, "rules.yml", `
714groups:
715- name: v2
716 rules:
717 - record: up:count
718 expr: count(up)
719`, "v2")
720 },
721 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
722 entries: []discovery.Entry{
723 {
724 State: discovery.Added,
725 Path: discovery.Path{
726 Name: "rules.yml",
727 SymlinkTarget: "rules.yml",
728 },
729 ModifiedLines: nil,
730 Rule: mustParse(4, " - record: up:count\n expr: count(up)\n"),
731 },
732 {
733 State: discovery.Removed,
734 Path: discovery.Path{
735 Name: "rules.yml",
736 SymlinkTarget: "rules.yml",
737 },
738 ModifiedLines: []int{5, 6, 7},
739 Rule: parser.Rule{
740 Lines: diags.LineRange{First: 5, Last: 7},
741 Error: parser.ParseError{
742 Line: 7,
743 Err: errors.New("duplicated expr key"),
744 },
745 },
746 },
747 },
748 },
749 {
750 title: "rules duplicated",
751 setup: func(t *testing.T) {
752 commitFile(t, "rules.yml", `
753- alert: rule1
754 expr: sum(foo) by(job)
755- alert: rule2
756 expr: sum(foo) by(job)
757`, "v1")
758
759 _, err := git.RunGit("checkout", "-b", "v2")
760 require.NoError(t, err, "git checkout v2")
761
762 commitFile(t, "rules.yml", `
763- alert: rule1
764 expr: sum(foo) by(job)
765- alert: rule2
766 expr: sum(foo) by(job)
767- alert: rule2
768 expr: sum(foo) by(job)
769- alert: rule1
770 expr: sum(foo) by(job)
771`, "v2")
772 },
773 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
774 entries: []discovery.Entry{
775 {
776 State: discovery.Noop,
777 Path: discovery.Path{
778 Name: "rules.yml",
779 SymlinkTarget: "rules.yml",
780 },
781 ModifiedLines: []int{},
782 Rule: mustParse(1, "- alert: rule1\n expr: sum(foo) by(job)\n"),
783 },
784 {
785 State: discovery.Noop,
786 Path: discovery.Path{
787 Name: "rules.yml",
788 SymlinkTarget: "rules.yml",
789 },
790 ModifiedLines: []int{},
791 Rule: mustParse(3, "- alert: rule2\n expr: sum(foo) by(job)\n"),
792 },
793 {
794 State: discovery.Added,
795 Path: discovery.Path{
796 Name: "rules.yml",
797 SymlinkTarget: "rules.yml",
798 },
799 ModifiedLines: []int{6, 7},
800 Rule: mustParse(5, "- alert: rule2\n expr: sum(foo) by(job)\n"),
801 },
802 {
803 State: discovery.Added,
804 Path: discovery.Path{
805 Name: "rules.yml",
806 SymlinkTarget: "rules.yml",
807 },
808 ModifiedLines: []int{8, 9},
809 Rule: mustParse(7, "- alert: rule1\n expr: sum(foo) by(job)\n"),
810 },
811 },
812 },
813 {
814 title: "rules duplicated with different query",
815 setup: func(t *testing.T) {
816 commitFile(t, "rules.yml", `
817- alert: rule1
818 expr: sum(foo) by(job)
819`, "v1")
820
821 _, err := git.RunGit("checkout", "-b", "v2")
822 require.NoError(t, err, "git checkout v2")
823
824 commitFile(t, "rules.yml", `
825- alert: rule1
826 expr: up == 0
827- alert: rule1
828 expr: up == 1
829- alert: rule1
830 expr: up != 0
831- alert: rule2
832 expr: sum(foo) by(job)
833`, "v2")
834 },
835 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
836 entries: []discovery.Entry{
837 {
838 State: discovery.Modified,
839 Path: discovery.Path{
840 Name: "rules.yml",
841 SymlinkTarget: "rules.yml",
842 },
843 ModifiedLines: []int{3},
844 Rule: mustParse(1, "- alert: rule1\n expr: up == 0\n"),
845 },
846 {
847 State: discovery.Added,
848 Path: discovery.Path{
849 Name: "rules.yml",
850 SymlinkTarget: "rules.yml",
851 },
852 ModifiedLines: []int{4, 5},
853 Rule: mustParse(3, "- alert: rule1\n expr: up == 1\n"),
854 },
855 {
856 State: discovery.Added,
857 Path: discovery.Path{
858 Name: "rules.yml",
859 SymlinkTarget: "rules.yml",
860 },
861 ModifiedLines: []int{6, 7},
862 Rule: mustParse(5, "- alert: rule1\n expr: up != 0\n"),
863 },
864 {
865 State: discovery.Added,
866 Path: discovery.Path{
867 Name: "rules.yml",
868 SymlinkTarget: "rules.yml",
869 },
870 ModifiedLines: []int{8},
871 Rule: mustParse(7, "- alert: rule2\n expr: sum(foo) by(job)\n"),
872 },
873 },
874 },
875 {
876 title: "rule changed - modified for and added extra lines",
877 setup: func(t *testing.T) {
878 commitFile(t, "rules.yml", `
879- alert: rule1
880 expr: sum(foo) by(job)
881 for: 1s
882- alert: rule2
883 expr: sum(foo) by(job)
884 for: 1s
885`, "v1")
886
887 _, err := git.RunGit("checkout", "-b", "v2")
888 require.NoError(t, err, "git checkout v2")
889
890 commitFile(t, "rules.yml", `
891- alert: rule1
892 expr: sum(foo) by(job)
893 for: 1s
894- alert: rule2
895 expr: sum(foo) by(job)
896 keep_firing_for: 5m
897 for: 0s
898 annotations:
899 foo: bar
900 labels:
901 foo: bar
902`, "v2")
903 },
904 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
905 entries: []discovery.Entry{
906 {
907 State: discovery.Noop,
908 Path: discovery.Path{
909 Name: "rules.yml",
910 SymlinkTarget: "rules.yml",
911 },
912 ModifiedLines: []int{},
913 Rule: mustParse(1, "- alert: rule1\n expr: sum(foo) by(job)\n for: 1s\n"),
914 },
915 {
916 State: discovery.Modified,
917 Path: discovery.Path{
918 Name: "rules.yml",
919 SymlinkTarget: "rules.yml",
920 },
921 ModifiedLines: []int{7, 8, 9, 10, 11, 12},
922 Rule: mustParse(4, "- alert: rule2\n expr: sum(foo) by(job)\n keep_firing_for: 5m\n for: 0s\n annotations:\n foo: bar\n labels:\n foo: bar\n"),
923 },
924 },
925 },
926 {
927 title: "rule file moved",
928 setup: func(t *testing.T) {
929 commitFile(t, "a.yml", `
930- alert: rule
931 expr: up == 0
932`, "v1")
933
934 _, err := git.RunGit("checkout", "-b", "v2")
935 require.NoError(t, err, "git checkout v2")
936
937 _, err = git.RunGit("mv", "a.yml", "b.yml")
938 require.NoError(t, err, "git mv")
939
940 gitCommit(t, "v2")
941 },
942 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
943 entries: []discovery.Entry{
944 {
945 State: discovery.Moved,
946 Path: discovery.Path{
947 Name: "b.yml",
948 SymlinkTarget: "b.yml",
949 },
950 ModifiedLines: []int{1, 2, 3},
951 Rule: mustParse(1, "- alert: rule\n expr: up == 0\n"),
952 },
953 },
954 },
955 {
956 title: "rule modified then the file renamed",
957 setup: func(t *testing.T) {
958 commitFile(t, "a.yml", `
959- alert: rule
960 # pint disable promql/series
961 expr: up == 0
962`, "v1")
963
964 _, err := git.RunGit("checkout", "-b", "v2")
965 require.NoError(t, err, "git checkout v2")
966
967 commitFile(t, "a.yml", `
968- alert: rule
969 expr: up == 0
970`, "v2")
971
972 _, err = git.RunGit("mv", "a.yml", "b.yml")
973 require.NoError(t, err, "git mv")
974
975 gitCommit(t, "v3")
976 },
977 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
978 entries: []discovery.Entry{
979 {
980 State: discovery.Moved,
981 Path: discovery.Path{
982 Name: "b.yml",
983 SymlinkTarget: "b.yml",
984 },
985 ModifiedLines: []int{1, 2, 3},
986 Rule: mustParse(1, "- alert: rule\n expr: up == 0\n"),
987 },
988 },
989 },
990 {
991 title: "rule file with symlink moved",
992 setup: func(t *testing.T) {
993 commitFile(t, "rules.yml", `
994- alert: rule
995 expr: up == 0
996`, "v1")
997
998 err := os.Symlink("rules.yml", "symlink.yml")
999 require.NoError(t, err, "symlink")
1000 _, err = git.RunGit("add", "symlink.yml")
1001 require.NoError(t, err, "git add")
1002 gitCommit(t, "v1")
1003
1004 _, err = git.RunGit("checkout", "-b", "v2")
1005 require.NoError(t, err, "git checkout v2")
1006
1007 _, err = git.RunGit("mv", "rules.yml", "new.yml")
1008 require.NoError(t, err, "git mv")
1009
1010 _, err = git.RunGit("rm", "-f", "symlink.yml")
1011 require.NoError(t, err, "git rm symlink")
1012
1013 gitCommit(t, "v2")
1014 },
1015 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
1016 entries: []discovery.Entry{
1017 {
1018 State: discovery.Moved,
1019 Path: discovery.Path{
1020 Name: "new.yml",
1021 SymlinkTarget: "new.yml",
1022 },
1023 ModifiedLines: []int{1, 2, 3},
1024 Rule: mustParse(1, "- alert: rule\n expr: up == 0\n"),
1025 },
1026 {
1027 State: discovery.Removed,
1028 Path: discovery.Path{
1029 Name: "symlink.yml",
1030 SymlinkTarget: "rules.yml",
1031 },
1032 ModifiedLines: []int{2, 3},
1033 Rule: mustParse(1, "- alert: rule\n expr: up == 0\n"),
1034 },
1035 },
1036 },
1037 {
1038 title: "rule broken",
1039 setup: func(t *testing.T) {
1040 commitFile(t, "rules.yml", `
1041groups:
1042- name: v1
1043 rules:
1044 - record: rule1
1045 expr: sum(up)
1046 - record: rule2
1047 expr: sum(up)
1048 - record: rule3
1049 expr: sum(up)
1050 - record: rule4
1051 expr: sum(up)
1052`, "v1")
1053
1054 _, err := git.RunGit("checkout", "-b", "v2")
1055 require.NoError(t, err, "git checkout v2")
1056
1057 commitFile(t, "rules.yml", `
1058groups:
1059- name: v2
1060 rules:
1061 - record: rule1
1062 expr: sum(up)
1063 - record: rule2
1064 expr: sum(up)
1065 - record: rule3
1066 expr: sum(up)
1067 +
1068 sum(up)
1069 - record: rule4
1070 expr: sum(up)
1071 - record: rule5
1072 expr: sum(up)
1073`, "v2")
1074 },
1075 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
1076 entries: []discovery.Entry{
1077 {
1078 State: discovery.Added,
1079 Path: discovery.Path{
1080 Name: "rules.yml",
1081 SymlinkTarget: "rules.yml",
1082 },
1083 ModifiedLines: []int{3, 11, 12, 15, 16},
1084 PathError: parser.ParseError{
1085 Line: 11,
1086 Err: errors.New("could not find expected ':'"),
1087 },
1088 },
1089 },
1090 },
1091 {
1092 title: "file/disable comment removed",
1093 setup: func(t *testing.T) {
1094 commitFile(t, "rules.yml", `
1095# pint file/disable promql/series
1096
1097- alert: rule1
1098 expr: sum(foo) by(job)
1099- alert: rule2
1100 expr: sum(foo) by(job)
1101- alert: rule3
1102 expr: sum(foo) by(job)
1103`, "v1")
1104
1105 _, err := git.RunGit("checkout", "-b", "v2")
1106 require.NoError(t, err, "git checkout v2")
1107
1108 commitFile(t, "rules.yml", `
1109
1110
1111- alert: rule1
1112 expr: sum(foo) by(job)
1113- alert: rule2
1114 expr: sum(foo) by(job)
1115- alert: rule3
1116 expr: sum(foo) by(job)
1117`, "v2")
1118 },
1119 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
1120 entries: []discovery.Entry{
1121 {
1122 State: discovery.Modified,
1123 Path: discovery.Path{
1124 Name: "rules.yml",
1125 SymlinkTarget: "rules.yml",
1126 },
1127 Rule: mustParse(3, "- alert: rule1\n expr: sum(foo) by(job)\n"),
1128 },
1129 {
1130 State: discovery.Modified,
1131 Path: discovery.Path{
1132 Name: "rules.yml",
1133 SymlinkTarget: "rules.yml",
1134 },
1135 Rule: mustParse(5, "- alert: rule2\n expr: sum(foo) by(job)\n"),
1136 },
1137 {
1138 State: discovery.Modified,
1139 Path: discovery.Path{
1140 Name: "rules.yml",
1141 SymlinkTarget: "rules.yml",
1142 },
1143 Rule: mustParse(7, "- alert: rule3\n expr: sum(foo) by(job)\n"),
1144 },
1145 },
1146 },
1147 {
1148 title: "add two dups",
1149 setup: func(t *testing.T) {
1150 commitFile(t, "rules.yml", `
1151- alert: rule1
1152 expr: sum(foo) by(job)
1153- alert: rule2
1154 expr: sum(foo) by(job)
1155- alert: rule3
1156 expr: sum(foo) by(job)
1157`, "v1")
1158
1159 _, err := git.RunGit("checkout", "-b", "v2")
1160 require.NoError(t, err, "git checkout v2")
1161
1162 commitFile(t, "rules.yml", `
1163- alert: rule1
1164 expr: sum(foo) by(job)
1165- alert: rule2
1166 expr: sum(foo) by(job)
1167- alert: rule3
1168 expr: sum(foo) by(job)
1169- alert: rule1
1170 expr: sum(foo) by(job)
1171- alert: rule1
1172 expr: sum(foo) by(job)
1173`, "v2")
1174 },
1175 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
1176 entries: []discovery.Entry{
1177 {
1178 State: discovery.Noop,
1179 Path: discovery.Path{
1180 Name: "rules.yml",
1181 SymlinkTarget: "rules.yml",
1182 },
1183 ModifiedLines: []int{},
1184 Rule: mustParse(1, "- alert: rule1\n expr: sum(foo) by(job)\n"),
1185 },
1186 {
1187 State: discovery.Noop,
1188 Path: discovery.Path{
1189 Name: "rules.yml",
1190 SymlinkTarget: "rules.yml",
1191 },
1192 ModifiedLines: []int{},
1193 Rule: mustParse(3, "- alert: rule2\n expr: sum(foo) by(job)\n"),
1194 },
1195 {
1196 State: discovery.Noop,
1197 Path: discovery.Path{
1198 Name: "rules.yml",
1199 SymlinkTarget: "rules.yml",
1200 },
1201 ModifiedLines: []int{},
1202 Rule: mustParse(5, "- alert: rule3\n expr: sum(foo) by(job)\n"),
1203 },
1204 {
1205 State: discovery.Added,
1206 Path: discovery.Path{
1207 Name: "rules.yml",
1208 SymlinkTarget: "rules.yml",
1209 },
1210 ModifiedLines: []int{8, 9},
1211 Rule: mustParse(7, "- alert: rule1\n expr: sum(foo) by(job)\n"),
1212 },
1213 {
1214 State: discovery.Added,
1215 Path: discovery.Path{
1216 Name: "rules.yml",
1217 SymlinkTarget: "rules.yml",
1218 },
1219 ModifiedLines: []int{10, 11},
1220 Rule: mustParse(9, "- alert: rule1\n expr: sum(foo) by(job)\n"),
1221 },
1222 },
1223 },
1224 {
1225 title: "rule comment modified",
1226 setup: func(t *testing.T) {
1227 commitFile(t, "rules.yml", `
1228groups:
1229- name: v1
1230 rules:
1231 - record: up:count
1232 pint disable promql/series(up)
1233 expr: sum(up)
1234`, "v1")
1235
1236 _, err := git.RunGit("checkout", "-b", "v2")
1237 require.NoError(t, err, "git checkout v2")
1238
1239 commitFile(t, "rules.yml", `
1240groups:
1241- name: v2
1242 rules:
1243 - record: up:count
1244 # pint disable promql/series(up)
1245 expr: sum(up)
1246`, "v2")
1247 },
1248 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
1249 entries: []discovery.Entry{
1250 {
1251 State: discovery.Added,
1252 Path: discovery.Path{
1253 Name: "rules.yml",
1254 SymlinkTarget: "rules.yml",
1255 },
1256 ModifiedLines: []int{6},
1257 Rule: mustParse(4, " - record: up:count\n # pint disable promql/series(up)\n expr: sum(up)\n"),
1258 },
1259 {
1260 State: discovery.Removed,
1261 Path: discovery.Path{
1262 Name: "rules.yml",
1263 SymlinkTarget: "rules.yml",
1264 },
1265 PathError: parser.ParseError{
1266 Err: errors.New("xxx"),
1267 Line: 6,
1268 },
1269 ModifiedLines: []int{3, 6},
1270 },
1271 },
1272 },
1273 {
1274 title: "rule partially replaced",
1275 setup: func(t *testing.T) {
1276 commitFile(t, "rules.yml", `
1277groups:
1278- name: v1
1279 rules:
1280 - record: up:sum
1281 expr: |
1282 sum(
1283 up
1284 )
1285`, "v1")
1286
1287 _, err := git.RunGit("checkout", "-b", "v2")
1288 require.NoError(t, err, "git checkout v2")
1289
1290 commitFile(t, "rules.yml", `
1291groups:
1292- name: v1
1293 rules:
1294 - record: up:count
1295 expr: |
1296 count(
1297 up
1298 )
1299`, "v2")
1300 },
1301 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
1302 entries: []discovery.Entry{
1303 {
1304 State: discovery.Added,
1305 Path: discovery.Path{
1306 Name: "rules.yml",
1307 SymlinkTarget: "rules.yml",
1308 },
1309 ModifiedLines: []int{5, 7},
1310 Rule: mustParse(4, " - record: up:count\n expr: |\n count(\n up\n )\n"),
1311 },
1312 {
1313 State: discovery.Removed,
1314 Path: discovery.Path{
1315 Name: "rules.yml",
1316 SymlinkTarget: "rules.yml",
1317 },
1318 ModifiedLines: []int{5, 7},
1319 Rule: mustParse(4, " - record: up:sum\n expr: |\n sum(\n up\n )\n"),
1320 },
1321 },
1322 },
1323 }
1324
1325 for _, tc := range testCases {
1326 t.Run(tc.title, func(t *testing.T) {
1327 dir := t.TempDir()
1328 t.Chdir(dir)
1329
1330 _, err := git.RunGit("init", "--initial-branch=main", ".")
1331 require.NoError(t, err, "git init")
1332
1333 tc.setup(t)
1334 entries, err := tc.finder.Find(nil)
1335 if tc.err != "" {
1336 require.EqualError(t, err, tc.err)
1337 } else {
1338 require.NoError(t, err, "tc.finder.Find()")
1339
1340 expected, err := json.MarshalIndent(tc.entries, "", " ")
1341 require.NoError(t, err, "json(expected)")
1342 got, err := json.MarshalIndent(entries, "", " ")
1343 require.NoError(t, err, "json(got)")
1344 if diff := cmp.Diff(string(expected), string(got)); diff != "" {
1345 t.Errorf("tc.finder.Find() returned wrong output (-want +got):\n%s", diff)
1346 return
1347 }
1348 }
1349 })
1350 }
1351}
1352