cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.81.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/discovery/glob_test.go

532lines · modecode

1package discovery_test
2
3import (
4 "encoding/json"
5 "errors"
6 "io/fs"
7 "os"
8 "path"
9 "regexp"
10 "strconv"
11 "strings"
12 "syscall"
13 "testing"
14
15 "github.com/stretchr/testify/require"
16
17 "github.com/cloudflare/pint/internal/discovery"
18 "github.com/cloudflare/pint/internal/git"
19 "github.com/cloudflare/pint/internal/parser"
20)
21
22func TestGlobPathFinder(t *testing.T) {
23 type testCaseT struct {
24 setup func(t *testing.T)
25 files map[string]string
26 symlinks map[string]string
27 err string
28 entries []*discovery.Entry
29 finder discovery.GlobFinder
30 }
31
32 p := parser.NewParser(parser.DefaultOptions)
33 testRuleBody := "# pint file/owner bob\n\n- record: foo\n expr: sum(foo)\n"
34 testFile := p.Parse(strings.NewReader(testRuleBody))
35 require.NoError(t, testFile.Error.Err)
36 testCases := []testCaseT{
37 {
38 files: map[string]string{},
39 finder: discovery.NewGlobFinder([]string{"[]"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
40 err: "failed to expand file path pattern []: syntax error in pattern",
41 },
42 {
43 files: map[string]string{},
44 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
45 err: "no matching files",
46 },
47 {
48 files: map[string]string{
49 ".git/config": "test",
50 "bar.yml": testRuleBody,
51 },
52 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, []*regexp.Regexp{regexp.MustCompile(".*")}), parser.DefaultOptions, nil),
53 entries: []*discovery.Entry{
54 {
55 State: discovery.Noop,
56 Path: discovery.Path{
57 Name: "bar.yml",
58 SymlinkTarget: "bar.yml",
59 },
60 Rule: testFile.Groups[0].Rules[0],
61 Owner: "bob",
62 },
63 },
64 },
65 {
66 files: map[string]string{
67 "bar.yml": testRuleBody,
68 },
69 symlinks: map[string]string{
70 ".git": "/nonexistent/target",
71 },
72 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
73 entries: []*discovery.Entry{
74 {
75 State: discovery.Noop,
76 Path: discovery.Path{
77 Name: ".git",
78 SymlinkTarget: ".git",
79 },
80 PathError: errors.New("this is a symlink but target file cannot be evaluated: lstat /nonexistent: no such file or directory"),
81 },
82 {
83 State: discovery.Noop,
84 Path: discovery.Path{
85 Name: "bar.yml",
86 SymlinkTarget: "bar.yml",
87 },
88 PathError: parser.ParseError{
89 Err: errors.New("YAML list is not allowed here, expected a YAML mapping"),
90 Line: 3,
91 },
92 Owner: "bob",
93 },
94 },
95 },
96 {
97 files: map[string]string{},
98 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
99 err: "no matching files",
100 },
101 {
102 files: map[string]string{},
103 finder: discovery.NewGlobFinder([]string{"foo/*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
104 err: "no matching files",
105 },
106 {
107 files: map[string]string{"bar.yml": testRuleBody},
108 finder: discovery.NewGlobFinder([]string{"foo/*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
109 err: "no matching files",
110 },
111 {
112 files: map[string]string{"bar.yml": testRuleBody},
113 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, []*regexp.Regexp{regexp.MustCompile(".*")}), parser.DefaultOptions, nil),
114 entries: []*discovery.Entry{
115 {
116 State: discovery.Noop,
117 Path: discovery.Path{
118 Name: "bar.yml",
119 SymlinkTarget: "bar.yml",
120 },
121 Rule: testFile.Groups[0].Rules[0],
122 Owner: "bob",
123 },
124 },
125 },
126 {
127 files: map[string]string{"foo/bar.yml": testRuleBody + "\n\n# pint file/owner alice\n"},
128 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, []*regexp.Regexp{regexp.MustCompile(".*")}), parser.DefaultOptions, nil),
129 entries: []*discovery.Entry{
130 {
131 State: discovery.Noop,
132 Path: discovery.Path{
133 Name: "foo/bar.yml",
134 SymlinkTarget: "foo/bar.yml",
135 },
136 Rule: testFile.Groups[0].Rules[0],
137 Owner: "alice",
138 },
139 },
140 },
141 {
142 files: map[string]string{"bar.yml": testRuleBody},
143 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
144 entries: []*discovery.Entry{
145 {
146 State: discovery.Noop,
147 Path: discovery.Path{
148 Name: "bar.yml",
149 SymlinkTarget: "bar.yml",
150 },
151 PathError: parser.ParseError{
152 Err: errors.New("YAML list is not allowed here, expected a YAML mapping"),
153 Line: 3,
154 },
155 Owner: "bob",
156 },
157 },
158 },
159 {
160 files: map[string]string{"bar.yml": "record:::{}\n expr: sum(foo)\n\n# pint file/owner bob\n"},
161 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, []*regexp.Regexp{regexp.MustCompile(".*")}), parser.DefaultOptions, nil),
162 entries: []*discovery.Entry{
163 {
164 State: discovery.Noop,
165 Path: discovery.Path{
166 Name: "bar.yml",
167 SymlinkTarget: "bar.yml",
168 },
169 PathError: parser.ParseError{
170 Err: errors.New("mapping values are not allowed in this context"),
171 Line: 2,
172 },
173 Owner: "bob",
174 },
175 },
176 },
177 {
178 files: map[string]string{"bar.yml": testRuleBody},
179 symlinks: map[string]string{"link.yml": "bar.yml"},
180 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
181 entries: []*discovery.Entry{
182 {
183 State: discovery.Noop,
184 Path: discovery.Path{
185 Name: "bar.yml",
186 SymlinkTarget: "bar.yml",
187 },
188 PathError: parser.ParseError{
189 Err: errors.New("YAML list is not allowed here, expected a YAML mapping"),
190 Line: 3,
191 },
192 Owner: "bob",
193 },
194 {
195 State: discovery.Noop,
196 Path: discovery.Path{
197 Name: "link.yml",
198 SymlinkTarget: "bar.yml",
199 },
200 PathError: parser.ParseError{
201 Err: errors.New("YAML list is not allowed here, expected a YAML mapping"),
202 Line: 3,
203 },
204 Owner: "bob",
205 },
206 },
207 },
208 {
209 files: map[string]string{"a/bar.yml": testRuleBody},
210 symlinks: map[string]string{
211 "b/link.yml": "../a/bar.yml",
212 "b/c/link.yml": "../../a/bar.yml",
213 },
214 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
215 entries: []*discovery.Entry{
216 {
217 State: discovery.Noop,
218 Path: discovery.Path{
219 Name: "a/bar.yml",
220 SymlinkTarget: "a/bar.yml",
221 },
222 PathError: parser.ParseError{
223 Err: errors.New("YAML list is not allowed here, expected a YAML mapping"),
224 Line: 3,
225 },
226 Owner: "bob",
227 },
228 {
229 State: discovery.Noop,
230 Path: discovery.Path{
231 Name: "b/c/link.yml",
232 SymlinkTarget: "a/bar.yml",
233 },
234 PathError: parser.ParseError{
235 Err: errors.New("YAML list is not allowed here, expected a YAML mapping"),
236 Line: 3,
237 },
238 Owner: "bob",
239 },
240 {
241 State: discovery.Noop,
242 Path: discovery.Path{
243 Name: "b/link.yml",
244 SymlinkTarget: "a/bar.yml",
245 },
246 PathError: parser.ParseError{
247 Err: errors.New("YAML list is not allowed here, expected a YAML mapping"),
248 Line: 3,
249 },
250 Owner: "bob",
251 },
252 },
253 },
254 {
255 files: map[string]string{"a/bar.yml": testRuleBody},
256 symlinks: map[string]string{
257 "b/link.yml": "../a/bar.yml",
258 "b/c/link.yml": "../a/bar.yml",
259 },
260 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
261 entries: []*discovery.Entry{
262 {
263 State: discovery.Noop,
264 Path: discovery.Path{
265 Name: "a/bar.yml",
266 SymlinkTarget: "a/bar.yml",
267 },
268 PathError: parser.ParseError{
269 Err: errors.New("YAML list is not allowed here, expected a YAML mapping"),
270 Line: 3,
271 },
272 Owner: "bob",
273 },
274 {
275 State: discovery.Noop,
276 Path: discovery.Path{
277 Name: "b/c/link.yml",
278 SymlinkTarget: "b/c/link.yml",
279 },
280 PathError: errors.New("this is a symlink but target file cannot be evaluated: lstat b/a: no such file or directory"),
281 },
282 {
283 State: discovery.Noop,
284 Path: discovery.Path{
285 Name: "b/link.yml",
286 SymlinkTarget: "a/bar.yml",
287 },
288 PathError: parser.ParseError{
289 Err: errors.New("YAML list is not allowed here, expected a YAML mapping"),
290 Line: 3,
291 },
292 Owner: "bob",
293 },
294 },
295 },
296 {
297 files: map[string]string{"a/bar.yml": "xxx:\n"},
298 symlinks: map[string]string{
299 "b/c/link.yml": "../../a/bar.yml",
300 },
301 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, []*regexp.Regexp{regexp.MustCompile(".*")}), parser.DefaultOptions, nil),
302 },
303 {
304 files: map[string]string{"a/bar.yml": "xxx:\nyyy:\n"},
305 symlinks: map[string]string{
306 "b/c/link.yml": "../../a/bar.yml",
307 },
308 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
309 entries: []*discovery.Entry{
310 {
311 State: discovery.Noop,
312 Path: discovery.Path{
313 Name: "a/bar.yml",
314 SymlinkTarget: "a/bar.yml",
315 },
316 PathError: parser.ParseError{
317 Err: errors.New("YAML list is not allowed here, expected a YAML mapping"),
318 Line: 1,
319 },
320 Owner: "",
321 },
322 {
323 State: discovery.Noop,
324 Path: discovery.Path{
325 Name: "b/c/link.yml",
326 SymlinkTarget: "a/bar.yml",
327 },
328 PathError: parser.ParseError{
329 Err: errors.New("YAML list is not allowed here, expected a YAML mapping"),
330 Line: 1,
331 },
332 Owner: "",
333 },
334 },
335 },
336 {
337 files: map[string]string{"a/bar.yml": "xxx:\nyyy:\n"},
338 symlinks: map[string]string{
339 "b/c/link.yml": "../../a/bar.yml",
340 },
341 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, []*regexp.Regexp{regexp.MustCompile(".*")}), parser.DefaultOptions, nil),
342 },
343 {
344 files: map[string]string{"a/bar.yml": "xxx:\nyyy:\n"},
345 symlinks: map[string]string{
346 "b/c/d": "../../a",
347 },
348 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, []*regexp.Regexp{regexp.MustCompile(".*")}), parser.DefaultOptions, nil),
349 },
350 {
351 files: map[string]string{"a/bar.yml": testRuleBody},
352 symlinks: map[string]string{
353 "b/c/link.yml": "../../a/bar.yml",
354 },
355 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
356 entries: []*discovery.Entry{
357 {
358 State: discovery.Noop,
359 Path: discovery.Path{
360 Name: "a/bar.yml",
361 SymlinkTarget: "a/bar.yml",
362 },
363 PathError: parser.ParseError{
364 Err: errors.New("YAML list is not allowed here, expected a YAML mapping"),
365 Line: 3,
366 },
367 Owner: "bob",
368 },
369 {
370 State: discovery.Noop,
371 Path: discovery.Path{
372 Name: "b/c/link.yml",
373 SymlinkTarget: "a/bar.yml",
374 },
375 PathError: parser.ParseError{
376 Err: errors.New("YAML list is not allowed here, expected a YAML mapping"),
377 Line: 3,
378 },
379 Owner: "bob",
380 },
381 },
382 },
383 {
384 symlinks: map[string]string{
385 "input.yml": "/xx/ccc/fdd",
386 },
387 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
388 entries: []*discovery.Entry{
389 {
390 State: discovery.Noop,
391 Path: discovery.Path{
392 Name: "input.yml",
393 SymlinkTarget: "input.yml",
394 },
395 PathError: errors.New("this is a symlink but target file cannot be evaluated: lstat /xx: no such file or directory"),
396 },
397 },
398 },
399 {
400 files: map[string]string{
401 "bar.yml": testRuleBody,
402 },
403 setup: func(_ *testing.T) {
404 _ = os.Chmod("bar.yml", 0o000)
405 },
406 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
407 entries: []*discovery.Entry{
408 {
409 State: discovery.Noop,
410 Path: discovery.Path{
411 Name: "bar.yml",
412 SymlinkTarget: "bar.yml",
413 },
414 PathError: &fs.PathError{Op: "open", Path: "bar.yml", Err: syscall.EACCES},
415 },
416 },
417 },
418 {
419 files: map[string]string{
420 "subdir/bar.yml": testRuleBody,
421 },
422 symlinks: map[string]string{
423 "subdir/broken.yml": "/nonexistent/target",
424 },
425 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
426 entries: []*discovery.Entry{
427 {
428 State: discovery.Noop,
429 Path: discovery.Path{
430 Name: "subdir/bar.yml",
431 SymlinkTarget: "subdir/bar.yml",
432 },
433 PathError: parser.ParseError{
434 Err: errors.New("YAML list is not allowed here, expected a YAML mapping"),
435 Line: 3,
436 },
437 Owner: "bob",
438 },
439 {
440 State: discovery.Noop,
441 Path: discovery.Path{
442 Name: "subdir/broken.yml",
443 SymlinkTarget: "subdir/broken.yml",
444 },
445 PathError: errors.New("this is a symlink but target file cannot be evaluated: lstat /nonexistent: no such file or directory"),
446 },
447 },
448 },
449 {
450 files: map[string]string{
451 "subdir/bar.yml": testRuleBody,
452 },
453 setup: func(t *testing.T) {
454 require.NoError(t, os.Chmod("subdir", 0o000))
455 t.Cleanup(func() { _ = os.Chmod("subdir", 0o755) })
456 },
457 finder: discovery.NewGlobFinder([]string{"*"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
458 err: "open subdir: permission denied",
459 },
460 {
461 // Symlink inside walked dir points to an unreadable directory.
462 // walkDir encounters the symlink, EvalSymlinks and os.Stat succeed,
463 // but recursive findFiles fails because the target dir is unreadable.
464 files: map[string]string{
465 "topdir/foo.yml": testRuleBody,
466 },
467 setup: func(t *testing.T) {
468 require.NoError(t, os.MkdirAll("targetdir", 0o755))
469 require.NoError(t, os.Symlink("../targetdir", "topdir/link"))
470 require.NoError(t, os.Chmod("targetdir", 0o000))
471 t.Cleanup(func() { _ = os.Chmod("targetdir", 0o755) })
472 },
473 finder: discovery.NewGlobFinder([]string{"topdir"}, git.NewPathFilter(nil, nil, nil), parser.DefaultOptions, nil),
474 err: "open targetdir: permission denied",
475 },
476 {
477 // File exists but path filter excludes it, no entries returned.
478 files: map[string]string{
479 "bar.yml": testRuleBody,
480 },
481 finder: discovery.NewGlobFinder(
482 []string{"*"},
483 git.NewPathFilter(
484 []*regexp.Regexp{regexp.MustCompile("^other/")},
485 nil,
486 nil,
487 ),
488 parser.DefaultOptions,
489 nil,
490 ),
491 },
492 }
493
494 for i, tc := range testCases {
495 t.Run(strconv.Itoa(i), func(t *testing.T) {
496 workdir := t.TempDir()
497 t.Chdir(workdir)
498
499 for p, content := range tc.files {
500 if strings.Contains(p, "/") {
501 err := os.MkdirAll(path.Dir(p), 0o755)
502 require.NoError(t, err)
503 }
504 err := os.WriteFile(p, []byte(content), 0o644)
505 require.NoError(t, err)
506 }
507 for symlink, target := range tc.symlinks {
508 if strings.Contains(symlink, "/") {
509 err := os.MkdirAll(path.Dir(symlink), 0o755)
510 require.NoError(t, err)
511 }
512 require.NoError(t, os.Symlink(target, symlink))
513 }
514 if tc.setup != nil {
515 tc.setup(t)
516 }
517
518 entries, err := tc.finder.Find()
519 if tc.err != "" {
520 require.EqualError(t, err, tc.err)
521 } else {
522 require.NoError(t, err)
523
524 expected, err := json.MarshalIndent(tc.entries, "", " ")
525 require.NoError(t, err, "json(expected)")
526 got, err := json.MarshalIndent(entries, "", " ")
527 require.NoError(t, err, "json(got)")
528 require.Equal(t, string(expected), string(got))
529 }
530 })
531 }
532}
533