cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.60.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/discovery/git_branch_test.go

1064lines · modecode

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