cloudflare/pint

Public

mirrored fromhttps://github.com/cloudflare/pintAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.75.0

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/git/changes_test.go

715lines · modecode

1package git_test
2
3import (
4 "fmt"
5 "log/slog"
6 "os"
7 "strings"
8 "testing"
9
10 "github.com/neilotoole/slogt"
11 "github.com/stretchr/testify/require"
12
13 "github.com/cloudflare/pint/internal/git"
14)
15
16func debugGitRun(t *testing.T) git.CommandRunner {
17 return func(args ...string) ([]byte, error) {
18 out, err := git.RunGit(args...)
19 if err == nil {
20 if len(out) == 0 {
21 t.Logf("%s ~> no stdout", strings.Join(args, " "))
22 } else {
23 t.Logf("%s\n---\n%s---", strings.Join(args, " "), string(out))
24 }
25 }
26 return out, err
27 }
28}
29
30func mustRun(t *testing.T, args ...string) {
31 _, err := debugGitRun(t)(args...)
32 require.NoError(t, err, strings.Join(args, " "))
33}
34
35func gitCommit(t *testing.T, message string) {
36 t.Setenv("GIT_AUTHOR_NAME", "pint")
37 t.Setenv("GIT_AUTHOR_EMAIL", "pint@example.com")
38 t.Setenv("GIT_COMMITTER_NAME", "pint")
39 t.Setenv("GIT_COMMITTER_EMAIL", "pint")
40 mustRun(t, "commit", "-am", "commit "+message)
41}
42
43func TestChanges(t *testing.T) {
44 type testCaseT struct {
45 setup func(t *testing.T) git.CommandRunner
46 title string
47 err string
48 changes []*git.FileChange
49 }
50
51 testCases := []testCaseT{
52 {
53 title: "git log error",
54 setup: func(_ *testing.T) git.CommandRunner {
55 cmd := func(args ...string) ([]byte, error) {
56 return nil, fmt.Errorf("mock git error: %v", args)
57 }
58 return cmd
59 },
60 changes: nil,
61 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]",
62 },
63 {
64 title: "chmod",
65 setup: func(t *testing.T) git.CommandRunner {
66 mustRun(t, "init", "--initial-branch=main", ".")
67 require.NoError(t, os.WriteFile("index.txt", []byte("foo"), 0o644))
68 mustRun(t, "add", "index.txt")
69 gitCommit(t, "init")
70
71 mustRun(t, "checkout", "-b", "v2")
72 require.NoError(t, os.Chmod("index.txt", 0o755))
73 mustRun(t, "add", "index.txt")
74 gitCommit(t, "chmod")
75
76 return debugGitRun(t)
77 },
78 changes: []*git.FileChange{
79 {
80 Commits: []string{"1"},
81 Path: git.PathDiff{
82 Before: git.Path{
83 Name: "index.txt",
84 Type: git.File,
85 },
86 After: git.Path{
87 Name: "index.txt",
88 Type: git.File,
89 },
90 },
91 Body: git.BodyDiff{
92 Before: []byte("foo"),
93 After: []byte("foo"),
94 ModifiedLines: []int{},
95 },
96 },
97 },
98 err: "",
99 },
100 {
101 title: "dir -> file",
102 setup: func(t *testing.T) git.CommandRunner {
103 mustRun(t, "init", "--initial-branch=main", ".")
104 require.NoError(t, os.Mkdir("index.txt", 0o755))
105 require.NoError(t, os.WriteFile("index.txt/.keep", []byte("keep"), 0o644))
106 mustRun(t, "add", "index.txt")
107 gitCommit(t, "init")
108
109 mustRun(t, "checkout", "-b", "v2")
110 require.NoError(t, os.RemoveAll("index.txt"))
111 require.NoError(t, os.WriteFile("index.txt", []byte("foo"), 0o644))
112 mustRun(t, "add", "index.txt")
113 gitCommit(t, "chmod")
114
115 return debugGitRun(t)
116 },
117 changes: []*git.FileChange{
118 {
119 Commits: []string{"1"},
120 Path: git.PathDiff{
121 Before: git.Path{
122 Name: "index.txt",
123 Type: git.Dir,
124 },
125 After: git.Path{
126 Name: "index.txt",
127 Type: git.File,
128 },
129 },
130 Body: git.BodyDiff{
131 Before: nil,
132 After: []byte("foo"),
133 ModifiedLines: []int{1},
134 },
135 },
136 {
137 Commits: []string{"1"},
138 Path: git.PathDiff{
139 Before: git.Path{
140 Name: "index.txt/.keep",
141 Type: git.File,
142 },
143 After: git.Path{
144 Name: "index.txt/.keep",
145 Type: git.Missing,
146 },
147 },
148 Body: git.BodyDiff{
149 Before: []byte("keep"),
150 After: nil,
151 ModifiedLines: []int{1},
152 },
153 },
154 },
155 err: "",
156 },
157 {
158 title: "delete and re-add",
159 setup: func(t *testing.T) git.CommandRunner {
160 mustRun(t, "init", "--initial-branch=main", ".")
161 require.NoError(t, os.WriteFile("index.txt", []byte("foo"), 0o644))
162 mustRun(t, "add", "index.txt")
163 gitCommit(t, "init")
164
165 mustRun(t, "checkout", "-b", "v2")
166 require.NoError(t, os.Remove("index.txt"))
167 mustRun(t, "add", "index.txt")
168 gitCommit(t, "rm")
169 require.NoError(t, os.WriteFile("index.txt", []byte("foo"), 0o644))
170 mustRun(t, "add", "index.txt")
171 gitCommit(t, "add")
172
173 return debugGitRun(t)
174 },
175 changes: []*git.FileChange{
176 {
177 Commits: []string{"1", "2"},
178 Path: git.PathDiff{
179 Before: git.Path{
180 Name: "index.txt",
181 Type: git.File,
182 },
183 After: git.Path{
184 Name: "index.txt",
185 Type: git.File,
186 },
187 },
188 Body: git.BodyDiff{
189 Before: []byte("foo"),
190 After: []byte("foo"),
191 ModifiedLines: []int{},
192 },
193 },
194 },
195 err: "",
196 },
197 {
198 title: "file -> symlink",
199 setup: func(t *testing.T) git.CommandRunner {
200 mustRun(t, "init", "--initial-branch=main", ".")
201 require.NoError(t, os.WriteFile("index.txt", []byte("foo\n1\n"), 0o644))
202 mustRun(t, "add", "index.txt")
203 require.NoError(t, os.WriteFile("second file.txt", []byte("bar\n1\n"), 0o644))
204 mustRun(t, "add", "second file.txt")
205 gitCommit(t, "init")
206
207 mustRun(t, "checkout", "-b", "v2")
208 require.NoError(t, os.Remove("second file.txt"))
209 require.NoError(t, os.Symlink("index.txt", "second file.txt"))
210 mustRun(t, "add", "second file.txt")
211 gitCommit(t, "symlink")
212
213 return debugGitRun(t)
214 },
215 changes: []*git.FileChange{
216 {
217 Commits: []string{"1"},
218 Path: git.PathDiff{
219 Before: git.Path{
220 Name: "second file.txt",
221 Type: git.File,
222 },
223 After: git.Path{
224 Name: "second file.txt",
225 Type: git.Symlink,
226 SymlinkTarget: "index.txt",
227 },
228 },
229 Body: git.BodyDiff{
230 Before: []byte("bar\n1\n"),
231 After: []byte("foo\n1\n"),
232 ModifiedLines: []int{1, 2},
233 },
234 },
235 },
236 err: "",
237 },
238 {
239 title: "rename partial",
240 setup: func(t *testing.T) git.CommandRunner {
241 mustRun(t, "init", "--initial-branch=main", ".")
242 require.NoError(t, os.WriteFile("index.txt", []byte("1\n2\n3\n4\n5\n6\n7\n8\n9\n"), 0o644))
243 mustRun(t, "add", "index.txt")
244 gitCommit(t, "init")
245
246 mustRun(t, "checkout", "-b", "v2")
247 mustRun(t, "mv", "index.txt", "second.txt")
248 require.NoError(t, os.WriteFile("second.txt", []byte("1\n2\n3\n4\n5\nX\nX\nX\nX\n"), 0o644))
249 mustRun(t, "add", "second.txt")
250 gitCommit(t, "mv")
251
252 return debugGitRun(t)
253 },
254 changes: []*git.FileChange{
255 {
256 Commits: []string{"1"},
257 Path: git.PathDiff{
258 Before: git.Path{
259 Name: "index.txt",
260 Type: git.File,
261 },
262 After: git.Path{
263 Name: "second.txt",
264 Type: git.File,
265 },
266 },
267 Body: git.BodyDiff{
268 Before: []byte("1\n2\n3\n4\n5\n6\n7\n8\n9\n"),
269 After: []byte("1\n2\n3\n4\n5\nX\nX\nX\nX\n"),
270 ModifiedLines: []int{6, 7, 8, 9},
271 },
272 },
273 },
274 err: "",
275 },
276 {
277 title: "rename 100% and edit",
278 setup: func(t *testing.T) git.CommandRunner {
279 mustRun(t, "init", "--initial-branch=main", ".")
280 require.NoError(t, os.WriteFile("index.txt", []byte("1\n2\n3\n4\n5\n6\n7\n8\n9\n"), 0o644))
281 mustRun(t, "add", "index.txt")
282 gitCommit(t, "init")
283
284 mustRun(t, "checkout", "-b", "v2")
285 mustRun(t, "mv", "index.txt", "second.txt")
286 gitCommit(t, "mv")
287 require.NoError(t, os.WriteFile("second.txt", []byte("1\n2\n3\n4\n5\nX\n7\n8\n9\n"), 0o644))
288 mustRun(t, "add", "second.txt")
289 gitCommit(t, "edit")
290
291 return debugGitRun(t)
292 },
293 changes: []*git.FileChange{
294 {
295 Commits: []string{"1", "2"},
296 Path: git.PathDiff{
297 Before: git.Path{
298 Name: "index.txt",
299 Type: git.File,
300 },
301 After: git.Path{
302 Name: "second.txt",
303 Type: git.File,
304 },
305 },
306 Body: git.BodyDiff{
307 Before: []byte("1\n2\n3\n4\n5\n6\n7\n8\n9\n"),
308 After: []byte("1\n2\n3\n4\n5\nX\n7\n8\n9\n"),
309 ModifiedLines: []int{6},
310 },
311 },
312 },
313 err: "",
314 },
315 {
316 title: "add file, add another",
317 setup: func(t *testing.T) git.CommandRunner {
318 mustRun(t, "init", "--initial-branch=main", ".")
319 require.NoError(t, os.WriteFile("index.txt", []byte("foo"), 0o644))
320 mustRun(t, "add", "index.txt")
321 gitCommit(t, "init")
322
323 mustRun(t, "checkout", "-b", "v2")
324 require.NoError(t, os.WriteFile("second.txt", []byte("second"), 0o644))
325 mustRun(t, "add", "second.txt")
326 require.NoError(t, os.WriteFile("third.txt", []byte("third"), 0o644))
327 mustRun(t, "add", "third.txt")
328 gitCommit(t, "add two more")
329
330 return debugGitRun(t)
331 },
332 changes: []*git.FileChange{
333 {
334 Commits: []string{"1"},
335 Path: git.PathDiff{
336 Before: git.Path{
337 Name: "",
338 Type: git.Missing,
339 },
340 After: git.Path{
341 Name: "second.txt",
342 Type: git.File,
343 },
344 },
345 Body: git.BodyDiff{
346 After: []byte("second"),
347 ModifiedLines: []int{1},
348 },
349 },
350 {
351 Commits: []string{"1"},
352 Path: git.PathDiff{
353 Before: git.Path{
354 Name: "",
355 Type: git.Missing,
356 },
357 After: git.Path{
358 Name: "third.txt",
359 Type: git.File,
360 },
361 },
362 Body: git.BodyDiff{
363 After: []byte("third"),
364 ModifiedLines: []int{1},
365 },
366 },
367 },
368 err: "",
369 },
370 {
371 title: "delete file",
372 setup: func(t *testing.T) git.CommandRunner {
373 mustRun(t, "init", "--initial-branch=main", ".")
374 require.NoError(t, os.WriteFile("index.txt", []byte("foo"), 0o644))
375 mustRun(t, "add", "index.txt")
376 require.NoError(t, os.WriteFile("second.txt", []byte("second"), 0o644))
377 mustRun(t, "add", "second.txt")
378 gitCommit(t, "init")
379
380 mustRun(t, "checkout", "-b", "v2")
381 require.NoError(t, os.Remove("second.txt"))
382 mustRun(t, "add", "second.txt")
383 gitCommit(t, "rm second")
384
385 return debugGitRun(t)
386 },
387 changes: []*git.FileChange{
388 {
389 Commits: []string{"1"},
390 Path: git.PathDiff{
391 Before: git.Path{
392 Name: "second.txt",
393 Type: git.File,
394 },
395 After: git.Path{
396 Name: "second.txt",
397 Type: git.Missing,
398 },
399 },
400 Body: git.BodyDiff{
401 Before: []byte("second"),
402 ModifiedLines: []int{1},
403 },
404 },
405 },
406 err: "",
407 },
408 {
409 title: "delete symlink",
410 setup: func(t *testing.T) git.CommandRunner {
411 mustRun(t, "init", "--initial-branch=main", ".")
412 require.NoError(t, os.WriteFile("index.txt", []byte("foo"), 0o644))
413 mustRun(t, "add", "index.txt")
414 require.NoError(t, os.Symlink("index.txt", "second.txt"))
415 mustRun(t, "add", "second.txt")
416 gitCommit(t, "init")
417
418 mustRun(t, "checkout", "-b", "v2")
419 require.NoError(t, os.Remove("second.txt"))
420 mustRun(t, "add", "second.txt")
421 gitCommit(t, "rm second")
422
423 return debugGitRun(t)
424 },
425 changes: []*git.FileChange{
426 {
427 Commits: []string{"1"},
428 Path: git.PathDiff{
429 Before: git.Path{
430 Name: "second.txt",
431 Type: git.Symlink,
432 SymlinkTarget: "index.txt",
433 },
434 After: git.Path{
435 Name: "second.txt",
436 Type: git.Missing,
437 },
438 },
439 Body: git.BodyDiff{
440 Before: []byte("foo"),
441 ModifiedLines: []int{1},
442 },
443 },
444 },
445 err: "",
446 },
447 {
448 title: "delete directory with symlinks",
449 setup: func(t *testing.T) git.CommandRunner {
450 mustRun(t, "init", "--initial-branch=main", ".")
451 require.NoError(t, os.WriteFile("index.txt", []byte("foo"), 0o644))
452 mustRun(t, "add", "index.txt")
453 require.NoError(t, os.Mkdir("dir", 0o755))
454 require.NoError(t, os.Symlink("../index.txt", "dir/first.txt"))
455 require.NoError(t, os.Symlink("../index.txt", "dir/second.txt"))
456 mustRun(t, "add", "dir")
457 gitCommit(t, "init")
458
459 mustRun(t, "checkout", "-b", "v2")
460 require.NoError(t, os.RemoveAll("dir"))
461 mustRun(t, "add", "dir")
462 gitCommit(t, "rm dir")
463
464 return debugGitRun(t)
465 },
466 changes: []*git.FileChange{
467 {
468 Commits: []string{"1"},
469 Path: git.PathDiff{
470 Before: git.Path{
471 Name: "dir/first.txt",
472 Type: git.Symlink,
473 SymlinkTarget: "index.txt",
474 },
475 After: git.Path{
476 Name: "dir/first.txt",
477 Type: git.Missing,
478 },
479 },
480 Body: git.BodyDiff{
481 Before: []byte("foo"),
482 ModifiedLines: []int{1},
483 },
484 },
485 {
486 Commits: []string{"1"},
487 Path: git.PathDiff{
488 Before: git.Path{
489 Name: "dir/second.txt",
490 Type: git.Symlink,
491 SymlinkTarget: "index.txt",
492 },
493 After: git.Path{
494 Name: "dir/second.txt",
495 Type: git.Missing,
496 },
497 },
498 Body: git.BodyDiff{
499 Before: []byte("foo"),
500 ModifiedLines: []int{1},
501 },
502 },
503 },
504 err: "",
505 },
506 {
507 title: "symlink target changed",
508 setup: func(t *testing.T) git.CommandRunner {
509 mustRun(t, "init", "--initial-branch=main", ".")
510 require.NoError(t, os.WriteFile("index.txt", []byte("foo\n1\n"), 0o644))
511 mustRun(t, "add", "index.txt")
512 require.NoError(t, os.WriteFile("second file.txt", []byte("bar\n1\n"), 0o644))
513 mustRun(t, "add", "second file.txt")
514 require.NoError(t, os.Mkdir("dir", 0o755))
515 require.NoError(t, os.Symlink("../index.txt", "dir/first.txt"))
516 require.NoError(t, os.Symlink("../second file.txt", "dir/second.txt"))
517 mustRun(t, "add", "dir")
518 gitCommit(t, "init")
519
520 mustRun(t, "checkout", "-b", "v2")
521 require.NoError(t, os.Remove("dir/second.txt"))
522 require.NoError(t, os.Symlink("first.txt", "dir/second.txt"))
523 mustRun(t, "add", "dir")
524 gitCommit(t, "symlink change")
525
526 return debugGitRun(t)
527 },
528 changes: []*git.FileChange{
529 {
530 Commits: []string{"1"},
531 Path: git.PathDiff{
532 Before: git.Path{
533 Name: "dir/second.txt",
534 Type: git.Symlink,
535 SymlinkTarget: "second file.txt",
536 },
537 After: git.Path{
538 Name: "dir/second.txt",
539 Type: git.Symlink,
540 SymlinkTarget: "index.txt",
541 },
542 },
543 Body: git.BodyDiff{
544 Before: []byte("bar\n1\n"),
545 After: []byte("foo\n1\n"),
546 ModifiedLines: []int{1, 2},
547 },
548 },
549 },
550 err: "",
551 },
552 {
553 title: "rule modified then file renamed",
554 setup: func(t *testing.T) git.CommandRunner {
555 mustRun(t, "init", "--initial-branch=main", ".")
556 require.NoError(t, os.WriteFile("main.txt", []byte("l1\nl2\nl3\n"), 0o644))
557 mustRun(t, "add", "main.txt")
558 gitCommit(t, "init")
559
560 mustRun(t, "checkout", "-b", "v2")
561 require.NoError(t, os.WriteFile("main.txt", []byte("l1\nl3\n"), 0o644))
562 mustRun(t, "add", "main.txt")
563 gitCommit(t, "edit")
564
565 mustRun(t, "mv", "main.txt", "pr.txt")
566 gitCommit(t, "rename")
567
568 return debugGitRun(t)
569 },
570 changes: []*git.FileChange{
571 {
572 Commits: []string{"1", "2"},
573 Path: git.PathDiff{
574 Before: git.Path{
575 Name: "main.txt",
576 Type: git.File,
577 },
578 After: git.Path{
579 Name: "pr.txt",
580 Type: git.File,
581 },
582 },
583 Body: git.BodyDiff{
584 Before: []byte("l1\nl2\nl3\n"),
585 After: []byte("l1\nl3\n"),
586 ModifiedLines: []int{1, 2},
587 },
588 },
589 },
590 err: "",
591 },
592 {
593 title: "directory_renamed",
594 setup: func(t *testing.T) git.CommandRunner {
595 mustRun(t, "init", "--initial-branch=main", ".")
596 require.NoError(t, os.Mkdir("dir1", 0o755))
597 require.NoError(t, os.Mkdir("dir1/rules", 0o755))
598 require.NoError(t, os.WriteFile("dir1/rules/file1.txt", []byte("a1\na2\na3\n"), 0o644))
599 require.NoError(t, os.WriteFile("dir1/rules/file2.txt", []byte("b1\nb2"), 0o644))
600 mustRun(t, "add", "dir1")
601 gitCommit(t, "init")
602
603 mustRun(t, "checkout", "-b", "v1")
604 mustRun(t, "mv", "dir1", "dir2")
605 gitCommit(t, "rename")
606
607 return debugGitRun(t)
608 },
609 changes: []*git.FileChange{
610 {
611 Commits: []string{"1"},
612 Path: git.PathDiff{
613 Before: git.Path{
614 Name: "dir1/rules/file1.txt",
615 Type: git.File,
616 },
617 After: git.Path{
618 Name: "dir2/rules/file1.txt",
619 Type: git.File,
620 },
621 },
622 Body: git.BodyDiff{
623 Before: []byte("a1\na2\na3\n"),
624 After: []byte("a1\na2\na3\n"),
625 ModifiedLines: []int{1, 2, 3},
626 },
627 },
628 {
629 Commits: []string{"1"},
630 Path: git.PathDiff{
631 Before: git.Path{
632 Name: "dir1/rules/file2.txt",
633 Type: git.File,
634 },
635 After: git.Path{
636 Name: "dir2/rules/file2.txt",
637 Type: git.File,
638 },
639 },
640 Body: git.BodyDiff{
641 Before: []byte("b1\nb2"),
642 After: []byte("b1\nb2"),
643 ModifiedLines: []int{1, 2},
644 },
645 },
646 },
647 err: "",
648 },
649 {
650 title: "rule partially replaced",
651 setup: func(t *testing.T) git.CommandRunner {
652 mustRun(t, "init", "--initial-branch=main", ".")
653 require.NoError(t, os.WriteFile("main.txt", []byte("l1\nl2\nl3\n"), 0o644))
654 mustRun(t, "add", "main.txt")
655 gitCommit(t, "init")
656
657 mustRun(t, "checkout", "-b", "v2")
658 require.NoError(t, os.WriteFile("main.txt", []byte("l1\nl3\n"), 0o644))
659 mustRun(t, "add", "main.txt")
660 gitCommit(t, "edit")
661
662 mustRun(t, "mv", "main.txt", "pr.txt")
663 gitCommit(t, "rename")
664
665 return debugGitRun(t)
666 },
667 changes: []*git.FileChange{
668 {
669 Commits: []string{"1", "2"},
670 Path: git.PathDiff{
671 Before: git.Path{
672 Name: "main.txt",
673 Type: git.File,
674 },
675 After: git.Path{
676 Name: "pr.txt",
677 Type: git.File,
678 },
679 },
680 Body: git.BodyDiff{
681 Before: []byte("l1\nl2\nl3\n"),
682 After: []byte("l1\nl3\n"),
683 ModifiedLines: []int{1, 2},
684 },
685 },
686 },
687 err: "",
688 },
689 }
690
691 for _, tc := range testCases {
692 t.Run(tc.title, func(t *testing.T) {
693 slog.SetDefault(slogt.New(t))
694
695 dir := t.TempDir()
696 t.Chdir(dir)
697
698 cmd := tc.setup(t)
699 changes, err := git.Changes(cmd, "main", git.NewPathFilter(nil, nil, nil))
700 if tc.err != "" {
701 require.EqualError(t, err, tc.err)
702 require.Nil(t, changes)
703 } else {
704 require.NoError(t, err)
705 require.Len(t, changes, len(tc.changes))
706 for i := range tc.changes {
707 require.Len(t, changes[i].Commits, len(tc.changes[i].Commits), "changes[%d].Commits", i)
708 require.Equal(t, tc.changes[i].Path, changes[i].Path, "changes[%d].Path", i)
709 require.Equal(t, tc.changes[i].Body, changes[i].Body, "changes[%d].Body", i)
710 }
711 require.Len(t, changes, len(tc.changes))
712 }
713 })
714 }
715}
716