cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.71.6

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/discovery/git_branch_test.go

1250lines · 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 r, err := p.Parse([]byte(strings.Repeat("\n", offset) + s))
45 if err != nil {
46 panic(fmt.Sprintf("failed to parse rule:\n---\n%s\n---\nerror: %s", s, err))
47 }
48 if len(r) != 1 {
49 panic(fmt.Sprintf("wrong number of rules returned: %d\n---\n%s\n---", len(r), s))
50 }
51 return r[0]
52 }
53
54 type setupFn func(t *testing.T)
55
56 type testCaseT struct {
57 setup setupFn
58 title string
59 err string
60 entries []discovery.Entry
61 finder discovery.GitBranchFinder
62 }
63
64 testCases := []testCaseT{
65 {
66 title: "git list PR commits error - main",
67 setup: func(_ *testing.T) {},
68 finder: discovery.NewGitBranchFinder(
69 func(args ...string) ([]byte, error) {
70 return nil, fmt.Errorf("mock git error: %v", args)
71 },
72 git.NewPathFilter(includeAll, nil, nil),
73 "main",
74 50,
75 parser.PrometheusSchema, model.UTF8Validation,
76 nil,
77 ),
78 entries: nil,
79 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]",
80 },
81 {
82 title: "git list PR commits error - master",
83 setup: func(_ *testing.T) {},
84 finder: discovery.NewGitBranchFinder(
85 func(args ...string) ([]byte, error) {
86 return nil, fmt.Errorf("mock git error: %v", args)
87 },
88 git.NewPathFilter(includeAll, nil, nil),
89 "master",
90 50,
91 parser.PrometheusSchema, model.UTF8Validation,
92 nil,
93 ),
94 entries: nil,
95 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]",
96 },
97 {
98 title: "too many commits",
99 setup: func(t *testing.T) {
100 commitFile(t, "rules.yml", "# v1\n", "v1")
101
102 _, err := git.RunGit("checkout", "-b", "v2")
103 require.NoError(t, err, "git checkout v2")
104
105 commitFile(t, "rules.yml", "# v2-1\n", "v2-1")
106 commitFile(t, "rules.yml", "# v2-2\n", "v2-2")
107 commitFile(t, "rules.yml", "# v2-3\n", "v2-3")
108 commitFile(t, "rules.yml", "# v2-4\n", "v2-4")
109 },
110 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 3, parser.PrometheusSchema, model.UTF8Validation, nil),
111 entries: nil,
112 err: "number of commits to check (4) is higher than maxCommits (3), exiting",
113 },
114 {
115 title: "git list modified files error",
116 setup: func(_ *testing.T) {},
117 finder: discovery.NewGitBranchFinder(
118 func(args ...string) ([]byte, error) {
119 switch strings.Join(args, " ") {
120 case "log --format=%H --no-abbrev-commit --reverse main..HEAD":
121 return []byte("c1\nc2\nc3\nc4\n"), nil
122 default:
123 return nil, fmt.Errorf("mock git error: %v", args)
124 }
125 },
126 git.NewPathFilter(includeAll, nil, nil),
127 "main",
128 4,
129 parser.PrometheusSchema, model.UTF8Validation,
130 nil,
131 ),
132 entries: nil,
133 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]",
134 },
135 {
136 title: "git get commit message error",
137 setup: func(_ *testing.T) {},
138 finder: discovery.NewGitBranchFinder(
139 func(args ...string) ([]byte, error) {
140 switch strings.Join(args, " ") {
141 case "log --reverse --no-merges --first-parent --format=%H --name-status main..HEAD":
142 return []byte("c1\nA\trules.yml\n"), nil
143 default:
144 return nil, fmt.Errorf("mock git error: %v", args)
145 }
146 },
147 git.NewPathFilter(includeAll, nil, nil),
148 "main",
149 4,
150 parser.PrometheusSchema, model.UTF8Validation,
151 nil,
152 ),
153 entries: nil,
154 err: "failed to get commit message for c1: mock git error: [show -s --format=%B c1]",
155 },
156 {
157 title: "git blame error",
158 setup: func(_ *testing.T) {},
159 finder: discovery.NewGitBranchFinder(
160 func(args ...string) ([]byte, error) {
161 switch strings.Join(args, " ") {
162 case "log --reverse --no-merges --first-parent --format=%H --name-status main..HEAD":
163 return []byte("c1\nA\trules.yml\n"), nil
164 case "ls-tree c1^ rules.yml":
165 return []byte("100644 blob c0\trules.yml"), nil
166 case "ls-tree c1 rules.yml":
167 return []byte("100644 blob c1\trules.yml"), nil
168 case "show -s --format=%B c1":
169 return []byte(""), nil
170 default:
171 return nil, fmt.Errorf("mock git error: %v", args)
172 }
173 },
174 git.NewPathFilter(includeAll, nil, nil),
175 "main",
176 4,
177 parser.PrometheusSchema, model.UTF8Validation,
178 nil,
179 ),
180 entries: nil,
181 err: "failed to run git blame for rules.yml: mock git error: [blame --line-porcelain c1 -- rules.yml]",
182 },
183 {
184 title: "no rules in file",
185 setup: func(t *testing.T) {
186 commitFile(t, "rules.yml", "# v1\n", "v1")
187
188 _, err := git.RunGit("checkout", "-b", "v2")
189 require.NoError(t, err, "git checkout v2")
190
191 commitFile(t, "rules.yml", "# v2\n", "v2")
192 },
193 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
194 entries: nil,
195 },
196 {
197 title: "no rule changes",
198 setup: func(t *testing.T) {
199 commitFile(t, "rules.yml", `
200groups:
201- name: v1
202 rules:
203 - record: up:count
204 expr: count(up == 1)
205`, "v1")
206
207 _, err := git.RunGit("checkout", "-b", "v2")
208 require.NoError(t, err, "git checkout v2")
209
210 commitFile(t, "rules.yml", `
211groups:
212- name: v2
213 rules:
214 - record: up:count
215 expr: count(up == 1)
216`, "v2")
217 },
218 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
219 entries: []discovery.Entry{
220 {
221 State: discovery.Noop,
222 Path: discovery.Path{
223 Name: "rules.yml",
224 SymlinkTarget: "rules.yml",
225 },
226 ModifiedLines: []int{},
227 Rule: mustParse(4, " - record: up:count\n expr: count(up == 1)\n"),
228 },
229 },
230 },
231 {
232 title: "rule changed - strict",
233 setup: func(t *testing.T) {
234 commitFile(t, "rules.yml", `
235groups:
236- name: v1
237 rules:
238 - record: up:count
239 expr: count(up)
240`, "v1")
241
242 _, err := git.RunGit("checkout", "-b", "v2")
243 require.NoError(t, err, "git checkout v2")
244
245 commitFile(t, "rules.yml", `
246groups:
247- name: v2
248 rules:
249 - record: up:count
250 expr: count(up == 1)
251`, "v2")
252 },
253 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
254 entries: []discovery.Entry{
255 {
256 State: discovery.Modified,
257 Path: discovery.Path{
258 Name: "rules.yml",
259 SymlinkTarget: "rules.yml",
260 },
261 ModifiedLines: []int{6},
262 Rule: mustParse(4, " - record: up:count\n expr: count(up == 1)\n"),
263 },
264 },
265 },
266 {
267 title: "rule changed - relaxed",
268 setup: func(t *testing.T) {
269 commitFile(t, "rules.yml", `
270- record: up:count
271 expr: count(up)
272`, "v1")
273
274 _, err := git.RunGit("checkout", "-b", "v2")
275 require.NoError(t, err, "git checkout v2")
276
277 commitFile(t, "rules.yml", `
278- record: up:count
279 expr: count(up == 1)
280`, "v2")
281 },
282 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
283 entries: []discovery.Entry{
284 {
285 State: discovery.Modified,
286 Path: discovery.Path{
287 Name: "rules.yml",
288 SymlinkTarget: "rules.yml",
289 },
290 ModifiedLines: []int{3},
291 Rule: mustParse(1, "- record: up:count\n expr: count(up == 1)\n"),
292 },
293 },
294 },
295 {
296 title: "rule changed - empty include list",
297 setup: func(t *testing.T) {
298 commitFile(t, "rules.yml", `
299- record: up:count
300 expr: count(up)
301`, "v1")
302
303 _, err := git.RunGit("checkout", "-b", "v2")
304 require.NoError(t, err, "git checkout v2")
305
306 commitFile(t, "rules.yml", `
307- record: up:count
308 expr: count(up == 1)
309`, "v2")
310 },
311 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(nil, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
312 entries: []discovery.Entry{
313 {
314 State: discovery.Modified,
315 Path: discovery.Path{
316 Name: "rules.yml",
317 SymlinkTarget: "rules.yml",
318 },
319 ModifiedLines: []int{3},
320 Rule: mustParse(1, "- record: up:count\n expr: count(up == 1)\n"),
321 },
322 },
323 },
324 {
325 title: "rule changed but file excluded",
326 setup: func(t *testing.T) {
327 commitFile(t, "rules.yml", `
328groups:
329- name: v1
330 rules:
331 - record: up:count
332 expr: count(up)
333`, "v1")
334
335 _, err := git.RunGit("checkout", "-b", "v2")
336 require.NoError(t, err, "git checkout v2")
337
338 commitFile(t, "rules.yml", `
339groups:
340- name: v2
341 rules:
342 - record: up:count
343 expr: count(up == 1)
344`, "v2")
345 },
346 finder: discovery.NewGitBranchFinder(
347 git.RunGit,
348 git.NewPathFilter([]*regexp.Regexp{regexp.MustCompile("^foo#")}, nil, nil),
349 "main",
350 4,
351 parser.PrometheusSchema, model.UTF8Validation,
352 nil,
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, parser.PrometheusSchema, model.UTF8Validation, nil),
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, parser.PrometheusSchema, model.UTF8Validation, nil),
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, parser.PrometheusSchema, model.UTF8Validation, nil),
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, parser.PrometheusSchema, model.UTF8Validation, nil),
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.Noop,
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, parser.PrometheusSchema, model.UTF8Validation, nil),
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.Noop,
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, parser.PrometheusSchema, model.UTF8Validation, nil),
581 entries: []discovery.Entry{
582 {
583 State: discovery.Noop,
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, parser.PrometheusSchema, model.UTF8Validation, nil),
621 entries: []discovery.Entry{
622 {
623 State: discovery.Noop,
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, parser.PrometheusSchema, model.UTF8Validation, nil),
665 entries: []discovery.Entry{
666 {
667 State: discovery.Noop,
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.Noop,
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, parser.PrometheusSchema, model.UTF8Validation, nil),
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{5, 6, 7},
736 Rule: parser.Rule{
737 Lines: diags.LineRange{First: 5, Last: 7},
738 Error: parser.ParseError{
739 Line: 7,
740 Err: errors.New("duplicated expr key"),
741 },
742 },
743 },
744 },
745 },
746 {
747 title: "rules duplicated",
748 setup: func(t *testing.T) {
749 commitFile(t, "rules.yml", `
750- alert: rule1
751 expr: sum(foo) by(job)
752- alert: rule2
753 expr: sum(foo) by(job)
754`, "v1")
755
756 _, err := git.RunGit("checkout", "-b", "v2")
757 require.NoError(t, err, "git checkout v2")
758
759 commitFile(t, "rules.yml", `
760- alert: rule1
761 expr: sum(foo) by(job)
762- alert: rule2
763 expr: sum(foo) by(job)
764- alert: rule2
765 expr: sum(foo) by(job)
766- alert: rule1
767 expr: sum(foo) by(job)
768`, "v2")
769 },
770 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
771 entries: []discovery.Entry{
772 {
773 State: discovery.Noop,
774 Path: discovery.Path{
775 Name: "rules.yml",
776 SymlinkTarget: "rules.yml",
777 },
778 ModifiedLines: []int{},
779 Rule: mustParse(1, "- alert: rule1\n expr: sum(foo) by(job)\n"),
780 },
781 {
782 State: discovery.Noop,
783 Path: discovery.Path{
784 Name: "rules.yml",
785 SymlinkTarget: "rules.yml",
786 },
787 ModifiedLines: []int{},
788 Rule: mustParse(3, "- alert: rule2\n expr: sum(foo) by(job)\n"),
789 },
790 {
791 State: discovery.Added,
792 Path: discovery.Path{
793 Name: "rules.yml",
794 SymlinkTarget: "rules.yml",
795 },
796 ModifiedLines: []int{6, 7},
797 Rule: mustParse(5, "- alert: rule2\n expr: sum(foo) by(job)\n"),
798 },
799 {
800 State: discovery.Added,
801 Path: discovery.Path{
802 Name: "rules.yml",
803 SymlinkTarget: "rules.yml",
804 },
805 ModifiedLines: []int{8, 9},
806 Rule: mustParse(7, "- alert: rule1\n expr: sum(foo) by(job)\n"),
807 },
808 },
809 },
810 {
811 title: "rules duplicated with different query",
812 setup: func(t *testing.T) {
813 commitFile(t, "rules.yml", `
814- alert: rule1
815 expr: sum(foo) by(job)
816`, "v1")
817
818 _, err := git.RunGit("checkout", "-b", "v2")
819 require.NoError(t, err, "git checkout v2")
820
821 commitFile(t, "rules.yml", `
822- alert: rule1
823 expr: up == 0
824- alert: rule1
825 expr: up == 1
826- alert: rule1
827 expr: up != 0
828- alert: rule2
829 expr: sum(foo) by(job)
830`, "v2")
831 },
832 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
833 entries: []discovery.Entry{
834 {
835 State: discovery.Modified,
836 Path: discovery.Path{
837 Name: "rules.yml",
838 SymlinkTarget: "rules.yml",
839 },
840 ModifiedLines: []int{3},
841 Rule: mustParse(1, "- alert: rule1\n expr: up == 0\n"),
842 },
843 {
844 State: discovery.Added,
845 Path: discovery.Path{
846 Name: "rules.yml",
847 SymlinkTarget: "rules.yml",
848 },
849 ModifiedLines: []int{4, 5},
850 Rule: mustParse(3, "- alert: rule1\n expr: up == 1\n"),
851 },
852 {
853 State: discovery.Added,
854 Path: discovery.Path{
855 Name: "rules.yml",
856 SymlinkTarget: "rules.yml",
857 },
858 ModifiedLines: []int{6, 7},
859 Rule: mustParse(5, "- alert: rule1\n expr: up != 0\n"),
860 },
861 {
862 State: discovery.Added,
863 Path: discovery.Path{
864 Name: "rules.yml",
865 SymlinkTarget: "rules.yml",
866 },
867 ModifiedLines: []int{8, 9},
868 Rule: mustParse(7, "- alert: rule2\n expr: sum(foo) by(job)\n"),
869 },
870 },
871 },
872 {
873 title: "rule changed - modified for and added extra lines",
874 setup: func(t *testing.T) {
875 commitFile(t, "rules.yml", `
876- alert: rule1
877 expr: sum(foo) by(job)
878 for: 1s
879- alert: rule2
880 expr: sum(foo) by(job)
881 for: 1s
882`, "v1")
883
884 _, err := git.RunGit("checkout", "-b", "v2")
885 require.NoError(t, err, "git checkout v2")
886
887 commitFile(t, "rules.yml", `
888- alert: rule1
889 expr: sum(foo) by(job)
890 for: 1s
891- alert: rule2
892 expr: sum(foo) by(job)
893 keep_firing_for: 5m
894 for: 0s
895 annotations:
896 foo: bar
897 labels:
898 foo: bar
899`, "v2")
900 },
901 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
902 entries: []discovery.Entry{
903 {
904 State: discovery.Noop,
905 Path: discovery.Path{
906 Name: "rules.yml",
907 SymlinkTarget: "rules.yml",
908 },
909 ModifiedLines: []int{},
910 Rule: mustParse(1, "- alert: rule1\n expr: sum(foo) by(job)\n for: 1s\n"),
911 },
912 {
913 State: discovery.Modified,
914 Path: discovery.Path{
915 Name: "rules.yml",
916 SymlinkTarget: "rules.yml",
917 },
918 ModifiedLines: []int{7, 8, 9, 10, 11, 12},
919 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"),
920 },
921 },
922 },
923 {
924 title: "rule file moved",
925 setup: func(t *testing.T) {
926 commitFile(t, "a.yml", `
927- alert: rule
928 expr: up == 0
929`, "v1")
930
931 _, err := git.RunGit("checkout", "-b", "v2")
932 require.NoError(t, err, "git checkout v2")
933
934 _, err = git.RunGit("mv", "a.yml", "b.yml")
935 require.NoError(t, err, "git mv")
936
937 gitCommit(t, "v2")
938 },
939 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
940 entries: []discovery.Entry{
941 {
942 State: discovery.Moved,
943 Path: discovery.Path{
944 Name: "b.yml",
945 SymlinkTarget: "b.yml",
946 },
947 ModifiedLines: []int{1, 2, 3},
948 Rule: mustParse(1, "- alert: rule\n expr: up == 0\n"),
949 },
950 },
951 },
952 {
953 title: "rule modified then the file renamed",
954 setup: func(t *testing.T) {
955 commitFile(t, "a.yml", `
956- alert: rule
957 # pint disable promql/series
958 expr: up == 0
959`, "v1")
960
961 _, err := git.RunGit("checkout", "-b", "v2")
962 require.NoError(t, err, "git checkout v2")
963
964 commitFile(t, "a.yml", `
965- alert: rule
966 expr: up == 0
967`, "v2")
968
969 _, err = git.RunGit("mv", "a.yml", "b.yml")
970 require.NoError(t, err, "git mv")
971
972 gitCommit(t, "v3")
973 },
974 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
975 entries: []discovery.Entry{
976 {
977 State: discovery.Moved,
978 Path: discovery.Path{
979 Name: "b.yml",
980 SymlinkTarget: "b.yml",
981 },
982 ModifiedLines: []int{1, 2, 3},
983 Rule: mustParse(1, "- alert: rule\n expr: up == 0\n"),
984 },
985 },
986 },
987 {
988 title: "rule file with symlink moved",
989 setup: func(t *testing.T) {
990 commitFile(t, "rules.yml", `
991- alert: rule
992 expr: up == 0
993`, "v1")
994
995 err := os.Symlink("rules.yml", "symlink.yml")
996 require.NoError(t, err, "symlink")
997 _, err = git.RunGit("add", "symlink.yml")
998 require.NoError(t, err, "git add")
999 gitCommit(t, "v1")
1000
1001 _, err = git.RunGit("checkout", "-b", "v2")
1002 require.NoError(t, err, "git checkout v2")
1003
1004 _, err = git.RunGit("mv", "rules.yml", "new.yml")
1005 require.NoError(t, err, "git mv")
1006
1007 _, err = git.RunGit("rm", "-f", "symlink.yml")
1008 require.NoError(t, err, "git rm symlink")
1009
1010 gitCommit(t, "v2")
1011 },
1012 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
1013 entries: []discovery.Entry{
1014 {
1015 State: discovery.Moved,
1016 Path: discovery.Path{
1017 Name: "new.yml",
1018 SymlinkTarget: "new.yml",
1019 },
1020 ModifiedLines: []int{1, 2, 3},
1021 Rule: mustParse(1, "- alert: rule\n expr: up == 0\n"),
1022 },
1023 {
1024 State: discovery.Removed,
1025 Path: discovery.Path{
1026 Name: "symlink.yml",
1027 SymlinkTarget: "rules.yml",
1028 },
1029 ModifiedLines: []int{2, 3},
1030 Rule: mustParse(1, "- alert: rule\n expr: up == 0\n"),
1031 },
1032 },
1033 },
1034 {
1035 title: "rule broken",
1036 setup: func(t *testing.T) {
1037 commitFile(t, "rules.yml", `
1038groups:
1039- name: v1
1040 rules:
1041 - record: rule1
1042 expr: sum(up)
1043 - record: rule2
1044 expr: sum(up)
1045 - record: rule3
1046 expr: sum(up)
1047 - record: rule4
1048 expr: sum(up)
1049`, "v1")
1050
1051 _, err := git.RunGit("checkout", "-b", "v2")
1052 require.NoError(t, err, "git checkout v2")
1053
1054 commitFile(t, "rules.yml", `
1055groups:
1056- name: v2
1057 rules:
1058 - record: rule1
1059 expr: sum(up)
1060 - record: rule2
1061 expr: sum(up)
1062 - record: rule3
1063 expr: sum(up)
1064 +
1065 sum(up)
1066 - record: rule4
1067 expr: sum(up)
1068 - record: rule5
1069 expr: sum(up)
1070`, "v2")
1071 },
1072 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, nil), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
1073 entries: []discovery.Entry{
1074 {
1075 State: discovery.Added,
1076 Path: discovery.Path{
1077 Name: "rules.yml",
1078 SymlinkTarget: "rules.yml",
1079 },
1080 ModifiedLines: []int{3, 11, 12, 13, 14, 15, 16},
1081 PathError: parser.ParseError{
1082 Line: 11,
1083 Err: errors.New("could not find expected ':'"),
1084 },
1085 },
1086 },
1087 },
1088 {
1089 title: "file/disable comment removed",
1090 setup: func(t *testing.T) {
1091 commitFile(t, "rules.yml", `
1092# pint file/disable promql/series
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`, "v1")
1101
1102 _, err := git.RunGit("checkout", "-b", "v2")
1103 require.NoError(t, err, "git checkout v2")
1104
1105 commitFile(t, "rules.yml", `
1106
1107
1108- alert: rule1
1109 expr: sum(foo) by(job)
1110- alert: rule2
1111 expr: sum(foo) by(job)
1112- alert: rule3
1113 expr: sum(foo) by(job)
1114`, "v2")
1115 },
1116 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
1117 entries: []discovery.Entry{
1118 {
1119 State: discovery.Modified,
1120 Path: discovery.Path{
1121 Name: "rules.yml",
1122 SymlinkTarget: "rules.yml",
1123 },
1124 Rule: mustParse(3, "- alert: rule1\n expr: sum(foo) by(job)\n"),
1125 },
1126 {
1127 State: discovery.Modified,
1128 Path: discovery.Path{
1129 Name: "rules.yml",
1130 SymlinkTarget: "rules.yml",
1131 },
1132 Rule: mustParse(5, "- alert: rule2\n expr: sum(foo) by(job)\n"),
1133 },
1134 {
1135 State: discovery.Modified,
1136 Path: discovery.Path{
1137 Name: "rules.yml",
1138 SymlinkTarget: "rules.yml",
1139 },
1140 Rule: mustParse(7, "- alert: rule3\n expr: sum(foo) by(job)\n"),
1141 },
1142 },
1143 },
1144 {
1145 title: "add two dups",
1146 setup: func(t *testing.T) {
1147 commitFile(t, "rules.yml", `
1148- alert: rule1
1149 expr: sum(foo) by(job)
1150- alert: rule2
1151 expr: sum(foo) by(job)
1152- alert: rule3
1153 expr: sum(foo) by(job)
1154`, "v1")
1155
1156 _, err := git.RunGit("checkout", "-b", "v2")
1157 require.NoError(t, err, "git checkout v2")
1158
1159 commitFile(t, "rules.yml", `
1160- alert: rule1
1161 expr: sum(foo) by(job)
1162- alert: rule2
1163 expr: sum(foo) by(job)
1164- alert: rule3
1165 expr: sum(foo) by(job)
1166- alert: rule1
1167 expr: sum(foo) by(job)
1168- alert: rule1
1169 expr: sum(foo) by(job)
1170`, "v2")
1171 },
1172 finder: discovery.NewGitBranchFinder(git.RunGit, git.NewPathFilter(includeAll, nil, includeAll), "main", 4, parser.PrometheusSchema, model.UTF8Validation, nil),
1173 entries: []discovery.Entry{
1174 {
1175 State: discovery.Noop,
1176 Path: discovery.Path{
1177 Name: "rules.yml",
1178 SymlinkTarget: "rules.yml",
1179 },
1180 ModifiedLines: []int{},
1181 Rule: mustParse(1, "- alert: rule1\n expr: sum(foo) by(job)\n"),
1182 },
1183 {
1184 State: discovery.Noop,
1185 Path: discovery.Path{
1186 Name: "rules.yml",
1187 SymlinkTarget: "rules.yml",
1188 },
1189 ModifiedLines: []int{},
1190 Rule: mustParse(3, "- alert: rule2\n expr: sum(foo) by(job)\n"),
1191 },
1192 {
1193 State: discovery.Noop,
1194 Path: discovery.Path{
1195 Name: "rules.yml",
1196 SymlinkTarget: "rules.yml",
1197 },
1198 ModifiedLines: []int{},
1199 Rule: mustParse(5, "- alert: rule3\n expr: sum(foo) by(job)\n"),
1200 },
1201 {
1202 State: discovery.Added,
1203 Path: discovery.Path{
1204 Name: "rules.yml",
1205 SymlinkTarget: "rules.yml",
1206 },
1207 ModifiedLines: []int{8, 9},
1208 Rule: mustParse(7, "- alert: rule1\n expr: sum(foo) by(job)\n"),
1209 },
1210 {
1211 State: discovery.Added,
1212 Path: discovery.Path{
1213 Name: "rules.yml",
1214 SymlinkTarget: "rules.yml",
1215 },
1216 ModifiedLines: []int{10, 11},
1217 Rule: mustParse(9, "- alert: rule1\n expr: sum(foo) by(job)\n"),
1218 },
1219 },
1220 },
1221 }
1222
1223 for _, tc := range testCases {
1224 t.Run(tc.title, func(t *testing.T) {
1225 dir := t.TempDir()
1226 err := os.Chdir(dir)
1227 require.NoError(t, err, "chdir")
1228
1229 _, err = git.RunGit("init", "--initial-branch=main", ".")
1230 require.NoError(t, err, "git init")
1231
1232 tc.setup(t)
1233 entries, err := tc.finder.Find(nil)
1234 if tc.err != "" {
1235 require.EqualError(t, err, tc.err)
1236 } else {
1237 require.NoError(t, err, "tc.finder.Find()")
1238
1239 expected, err := json.MarshalIndent(tc.entries, "", " ")
1240 require.NoError(t, err, "json(expected)")
1241 got, err := json.MarshalIndent(entries, "", " ")
1242 require.NoError(t, err, "json(got)")
1243 if diff := cmp.Diff(string(expected), string(got)); diff != "" {
1244 t.Errorf("tc.finder.Find() returned wrong output (-want +got):\n%s", diff)
1245 return
1246 }
1247 }
1248 })
1249 }
1250}
1251