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/git/changes_test.go

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