cloudflare/pint

Public

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

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v0.57.3

Branches

Tags

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

Clone

HTTPS

Download ZIP

internal/git/changes_test.go

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