cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.65.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/discovery/git_branch_test.go

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