cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.17.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/discovery/git_branch_test.go

463lines · modecode

1package discovery_test
2
3import (
4 "fmt"
5 "io/ioutil"
6 "os"
7 "path"
8 "regexp"
9 "strconv"
10 "strings"
11 "testing"
12
13 "github.com/stretchr/testify/require"
14
15 "github.com/cloudflare/pint/internal/discovery"
16)
17
18func blameLine(sha string, line int, filename string) string {
19 return fmt.Sprintf(`%s %d %d 1
20author Alice Mock
21author-mail <alice@example.com>
22author-time 1559927997
23author-tz 0000
24committer Alice Mock
25committer-mail <alice@example.com>
26committer-time 1559927997
27committer-tz 0000
28summary Mock commit title
29boundary
30filename %s
31 fake content
32`, sha, line, line, filename)
33}
34
35type blameRange struct {
36 sha string
37 lines []int
38}
39
40func blame(v map[string][]blameRange) []byte {
41 var out string
42 for path, brs := range v {
43 for _, br := range brs {
44 for _, l := range br.lines {
45 out += blameLine(br.sha, l, path)
46 }
47 }
48 }
49 return []byte(out)
50}
51
52func TestGitBranchFinder(t *testing.T) {
53 type testCaseT struct {
54 files map[string]string
55 finder discovery.GitBranchFinder
56 rules map[string][]string
57 err string
58 }
59
60 testRuleBody := `
61- record: first
62 expr: sum(foo)
63
64- alert: second
65 expr: foo > bar
66 labels:
67 cluster: dev
68
69- record: third
70 expr: count(foo)
71 labels:
72 cluster: dev
73`
74
75 testCases := []testCaseT{
76 {
77 files: map[string]string{},
78 finder: discovery.NewGitBranchFinder(
79 func(args ...string) ([]byte, error) {
80 return nil, fmt.Errorf("mock error")
81 },
82 nil,
83 "main",
84 0,
85 nil,
86 ),
87 err: "failed to get the list of commits to scan: mock error",
88 },
89 {
90 files: map[string]string{},
91 finder: discovery.NewGitBranchFinder(
92 func(args ...string) ([]byte, error) {
93 switch strings.Join(args, " ") {
94 case "log --format=%H --no-abbrev-commit --reverse main..HEAD":
95 return []byte("commit1\n"), nil
96 default:
97 return nil, fmt.Errorf("mock error")
98 }
99 },
100 nil,
101 "main",
102 0,
103 nil,
104 ),
105 err: "failed to get the list of modified files from git: mock error",
106 },
107 {
108 files: map[string]string{},
109 finder: discovery.NewGitBranchFinder(
110 func(args ...string) ([]byte, error) {
111 switch strings.Join(args, " ") {
112 case "log --format=%H --no-abbrev-commit --reverse main..HEAD":
113 return []byte("commit1\n"), nil
114 case "log --reverse --no-merges --pretty=format:%H --name-status commit1^..commit1":
115 return []byte("commit1\nM foo.yml\n"), nil
116 case "show -s --format=%B commit1":
117 return nil, fmt.Errorf("mock error")
118 default:
119 t.Errorf("unknown args: %v", args)
120 t.FailNow()
121 return nil, nil
122 }
123 },
124 nil,
125 "main",
126 0,
127 []*regexp.Regexp{regexp.MustCompile(".*")},
128 ),
129 err: "failed to get commit message for commit1: mock error",
130 },
131 {
132 files: map[string]string{},
133 finder: discovery.NewGitBranchFinder(
134 func(args ...string) ([]byte, error) {
135 switch strings.Join(args, " ") {
136 case "log --format=%H --no-abbrev-commit --reverse main..HEAD":
137 return []byte("commit1\n"), nil
138 case "log --reverse --no-merges --pretty=format:%H --name-status commit1^..commit1":
139 return []byte("commit1\nM foo.yml\n"), nil
140 case "show -s --format=%B commit1":
141 return []byte("foo"), nil
142 case "blame --line-porcelain -- foo.yml":
143 return nil, fmt.Errorf("mock error")
144 default:
145 t.Errorf("unknown args: %v", args)
146 t.FailNow()
147 return nil, nil
148 }
149 },
150 nil,
151 "main",
152 0,
153 []*regexp.Regexp{regexp.MustCompile(".*")},
154 ),
155 err: "failed to run git blame for foo.yml: mock error",
156 },
157 {
158 files: map[string]string{},
159 finder: discovery.NewGitBranchFinder(
160 func(args ...string) ([]byte, error) {
161 switch strings.Join(args, " ") {
162 case "log --format=%H --no-abbrev-commit --reverse main..HEAD":
163 return []byte("commit1\n"), nil
164 case "log --reverse --no-merges --pretty=format:%H --name-status commit1^..commit1":
165 return []byte("commit1\nM foo.yml\n"), nil
166 case "show -s --format=%B commit1":
167 return []byte("foo"), nil
168 case "blame --line-porcelain -- foo.yml":
169 return blame(map[string][]blameRange{
170 "foo.yml": {
171 {sha: "commitX", lines: []int{1, 3, 4, 5, 6, 9, 10, 11, 12}},
172 {sha: "commit1", lines: []int{2, 7, 8}},
173 },
174 }), nil
175 default:
176 t.Errorf("unknown args: %v", args)
177 t.FailNow()
178 return nil, nil
179 }
180 },
181 nil,
182 "main",
183 0,
184 []*regexp.Regexp{regexp.MustCompile(".*")},
185 ),
186 err: "open foo.yml: no such file or directory",
187 },
188 {
189 files: map[string]string{
190 "foo.yml": testRuleBody,
191 },
192 finder: discovery.NewGitBranchFinder(
193 func(args ...string) ([]byte, error) {
194 switch strings.Join(args, " ") {
195 case "log --format=%H --no-abbrev-commit --reverse main..HEAD":
196 return []byte("commit1\n"), nil
197 case "log --reverse --no-merges --pretty=format:%H --name-status commit1^..commit1":
198 return []byte("commit1\nM foo.yml\n"), nil
199 case "show -s --format=%B commit1":
200 return []byte("foo"), nil
201 case "blame --line-porcelain -- foo.yml":
202 return blame(map[string][]blameRange{
203 "foo.yml": {
204 {sha: "commitX", lines: []int{1, 3, 4, 5, 6, 9, 10, 11, 12}},
205 {sha: "commit1", lines: []int{2, 7, 8}},
206 },
207 }), nil
208 default:
209 t.Errorf("unknown args: %v", args)
210 t.FailNow()
211 return nil, nil
212 }
213 },
214 nil,
215 "main",
216 0,
217 []*regexp.Regexp{regexp.MustCompile(".*")},
218 ),
219 rules: map[string][]string{"foo.yml": {"first", "second"}},
220 },
221 {
222 files: map[string]string{
223 "foo/i1.yml": testRuleBody,
224 "i2.yml": testRuleBody,
225 "foo/c1a.yml": testRuleBody,
226 "foo/c1b.yml": testRuleBody,
227 "c2a.yml": testRuleBody,
228 "c2c.yml": testRuleBody,
229 "foo/c2b.yml": testRuleBody,
230 "bar/c3a.yml": testRuleBody,
231 "c3b.yml": testRuleBody,
232 "c3c.yml": testRuleBody,
233 "c3d.yml": testRuleBody,
234 "c3e.yml": testRuleBody,
235 },
236 finder: discovery.NewGitBranchFinder(
237 func(args ...string) ([]byte, error) {
238 switch strings.Join(args, " ") {
239 case "log --format=%H --no-abbrev-commit --reverse main..HEAD":
240 return []byte("commit1\ncommit2\ncommit3\n"), nil
241 case "log --reverse --no-merges --pretty=format:%H --name-status commit1^..commit3":
242 return []byte(`commit1
243M foo/c1a.yml
244M foo/c1b.yml
245commit2
246M c2a.yml
247M foo/c2b.yml
248A foo/c2c.yml
249commit3
250
251A bar/c3a.yml
252R053 src.txt c3b.yml
253R100 foo/c3c.txt c3c.yml
254M c2a.yml
255C50 foo/cp1.yml c3d.yml
256D foo/c2b.yml
257R090 foo/c2c.yml c2c.yml
258`), nil
259 case "show -s --format=%B commit1":
260 return []byte("foo"), nil
261 case "show -s --format=%B commit2":
262 return []byte("bar"), nil
263 case "show -s --format=%B commit3":
264 return []byte("foo"), nil
265 case "blame --line-porcelain -- foo/c1a.yml":
266 return blame(map[string][]blameRange{
267 "foo/c1a.yml": {
268 {sha: "commit1", lines: []int{2, 12}}, // 1 & 3
269 {sha: "commitX", lines: []int{1, 3, 4, 5, 6, 7, 8, 9, 10, 11}},
270 },
271 }), nil
272 case "blame --line-porcelain -- foo/c1b.yml":
273 return blame(map[string][]blameRange{
274 "foo/c1b.yml": {
275 {sha: "commit1", lines: []int{11, 12}}, // 3
276 {sha: "commitX", lines: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}},
277 },
278 }), nil
279 case "blame --line-porcelain -- c2a.yml":
280 return blame(map[string][]blameRange{
281 "c2a.yml": {
282 {sha: "commitX", lines: []int{1, 2, 3, 4, 5, 6, 9, 11, 12}},
283 {sha: "commit2", lines: []int{7, 8, 10}}, // 2 & 3
284 {sha: "commit3", lines: []int{3}}, // 1
285 },
286 }), nil
287 case "blame --line-porcelain -- c2c.yml":
288 return blame(map[string][]blameRange{
289 "c2c.yml": {
290 {sha: "commit2", lines: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}}, // 2 & 3
291 {sha: "commit3", lines: []int{3}}, // 1
292 },
293 }), nil
294 case "blame --line-porcelain -- c3b.yml":
295 return blame(map[string][]blameRange{
296 "c3b.yml": {
297 {sha: "commitX", lines: []int{1, 11, 12}},
298 {sha: "commit3", lines: []int{2, 3, 4, 5, 6, 7, 8, 9, 10}}, // 1 & 2 & 3
299 },
300 }), nil
301 case "blame --line-porcelain -- c3c.yml":
302 return blame(map[string][]blameRange{
303 "c3c.yml": {
304 {sha: "commit3", lines: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}}, // 1 & 2 & 3
305 },
306 }), nil
307 case "blame --line-porcelain -- c3d.yml":
308 return blame(map[string][]blameRange{
309 "c3d.yml": {
310 {sha: "commitX", lines: []int{1, 11, 12}},
311 {sha: "commit3", lines: []int{2, 3, 4, 5, 6, 7, 8, 9, 10}}, // 1 & 2 & 3
312 },
313 }), nil
314 default:
315 t.Errorf("unknown args: %v", args)
316 t.FailNow()
317 return nil, nil
318 }
319 },
320 []*regexp.Regexp{
321 regexp.MustCompile("^foo/.*"),
322 regexp.MustCompile("^c.*.yml$"),
323 },
324 "main",
325 0,
326 []*regexp.Regexp{regexp.MustCompile(".*")},
327 ),
328 rules: map[string][]string{
329 "foo/c1a.yml": {"first", "third"},
330 "foo/c1b.yml": {"third"},
331 "c2a.yml": {"first", "second", "third"},
332 "c2c.yml": {"first", "second", "third"},
333 "c3b.yml": {"first", "second", "third"},
334 "c3c.yml": {"first", "second", "third"},
335 "c3d.yml": {"first", "second", "third"},
336 },
337 },
338 {
339 files: map[string]string{
340 "foo.yml": testRuleBody,
341 },
342 finder: discovery.NewGitBranchFinder(
343 func(args ...string) ([]byte, error) {
344 switch strings.Join(args, " ") {
345 case "log --format=%H --no-abbrev-commit --reverse main..HEAD":
346 return []byte("commit1\n"), nil
347 case "log --reverse --no-merges --pretty=format:%H --name-status commit1^..commit1":
348 return []byte("commit1\nM foo.yml\n"), nil
349 case "show -s --format=%B commit1":
350 return []byte("foo"), nil
351 case "blame --line-porcelain -- foo.yml":
352 return blame(map[string][]blameRange{
353 "foo.yml": {
354 {sha: "commitX", lines: []int{1, 3, 4, 5, 6, 9, 10, 11, 12}},
355 {sha: "commit1", lines: []int{2, 7, 8}},
356 },
357 }), nil
358 default:
359 t.Errorf("unknown args: %v", args)
360 t.FailNow()
361 return nil, nil
362 }
363 },
364 nil,
365 "main",
366 0,
367 nil,
368 ),
369 rules: map[string][]string{},
370 },
371 {
372 files: map[string]string{
373 "foo.yml": testRuleBody,
374 },
375 finder: discovery.NewGitBranchFinder(
376 func(args ...string) ([]byte, error) {
377 switch strings.Join(args, " ") {
378 case "log --format=%H --no-abbrev-commit --reverse main..HEAD":
379 return []byte("commit1\n"), nil
380 case "log --reverse --no-merges --pretty=format:%H --name-status commit1^..commit1":
381 return []byte("commit1\nM foo.yml\n"), nil
382 case "show -s --format=%B commit1":
383 return []byte("foo [skip ci] bar"), nil
384 default:
385 t.Errorf("unknown args: %v", args)
386 t.FailNow()
387 return nil, nil
388 }
389 },
390 nil,
391 "main",
392 0,
393 []*regexp.Regexp{regexp.MustCompile(".*")},
394 ),
395 rules: map[string][]string{},
396 },
397 {
398 files: map[string]string{
399 "foo.yml": testRuleBody,
400 },
401 finder: discovery.NewGitBranchFinder(
402 func(args ...string) ([]byte, error) {
403 switch strings.Join(args, " ") {
404 case "log --format=%H --no-abbrev-commit --reverse main..HEAD":
405 return []byte("commit1\n"), nil
406 case "log --reverse --no-merges --pretty=format:%H --name-status commit1^..commit1":
407 return []byte("commit1\nM foo.yml\n"), nil
408 case "show -s --format=%B commit1":
409 return []byte("foo [no ci] bar"), nil
410 default:
411 t.Errorf("unknown args: %v", args)
412 t.FailNow()
413 return nil, nil
414 }
415 },
416 nil,
417 "main",
418 0,
419 []*regexp.Regexp{regexp.MustCompile(".*")},
420 ),
421 rules: map[string][]string{},
422 },
423 }
424
425 for i, tc := range testCases {
426 t.Run(strconv.Itoa(i), func(t *testing.T) {
427 workdir := t.TempDir()
428 err := os.Chdir(workdir)
429 require.NoError(t, err)
430
431 for p, content := range tc.files {
432 if strings.Contains(p, "/") {
433 err = os.MkdirAll(path.Dir(p), 0o755)
434 require.NoError(t, err)
435 }
436 err = ioutil.WriteFile(p, []byte(content), 0o644)
437 require.NoError(t, err)
438 }
439
440 entries, err := tc.finder.Find()
441 if tc.err != "" {
442 require.EqualError(t, err, tc.err)
443 } else {
444 require.NoError(t, err)
445
446 m := map[string][]string{}
447 for _, e := range entries {
448 if _, ok := m[e.Path]; !ok {
449 m[e.Path] = []string{}
450 }
451 var name string
452 if e.Rule.AlertingRule != nil {
453 name = e.Rule.AlertingRule.Alert.Value.Value
454 } else {
455 name = e.Rule.RecordingRule.Record.Value.Value
456 }
457 m[e.Path] = append(m[e.Path], name)
458 }
459 require.Equal(t, tc.rules, m)
460 }
461 })
462 }
463}
464