sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/git/git_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // This test imports "sigs.k8s.io/prow/pkg/git/localgit", which also reference 18 // "sigs.k8s.io/prow/pkg/git", has to be a separate package to avoid 19 // circular dependency(however this file implicitly referencing "sigs.k8s.io/prow/pkg/git") 20 package git_test 21 22 import ( 23 "bytes" 24 "fmt" 25 "os" 26 "os/exec" 27 "path/filepath" 28 "testing" 29 30 "github.com/sirupsen/logrus" 31 32 "sigs.k8s.io/prow/pkg/git/localgit" 33 "sigs.k8s.io/prow/pkg/git/types" 34 ) 35 36 var defaultBranch = localgit.DefaultBranch("") 37 38 func TestCloneV2(t *testing.T) { 39 testClone(localgit.NewV2, t) 40 } 41 42 func testClone(clients localgit.Clients, t *testing.T) { 43 lg, c, err := clients() 44 if err != nil { 45 t.Fatalf("Making local git repo: %v", err) 46 } 47 defer func() { 48 if err := lg.Clean(); err != nil { 49 t.Errorf("Error cleaning LocalGit: %v", err) 50 } 51 if err := c.Clean(); err != nil { 52 t.Errorf("Error cleaning Client: %v", err) 53 } 54 }() 55 if err := lg.MakeFakeRepo("foo", "bar"); err != nil { 56 t.Fatalf("Making fake repo: %v", err) 57 } 58 if err := lg.MakeFakeRepo("foo", "baz"); err != nil { 59 t.Fatalf("Making fake repo: %v", err) 60 } 61 62 // Fresh clone, will be a cache miss. 63 r1, err := c.ClientFor("foo", "bar") 64 if err != nil { 65 t.Fatalf("Cloning the first time: %v", err) 66 } 67 defer func() { 68 if err := r1.Clean(); err != nil { 69 t.Errorf("Cleaning repo: %v", err) 70 } 71 }() 72 73 // Clone from the same org. 74 r2, err := c.ClientFor("foo", "baz") 75 if err != nil { 76 t.Fatalf("Cloning another repo in the same org: %v", err) 77 } 78 defer func() { 79 if err := r2.Clean(); err != nil { 80 t.Errorf("Cleaning repo: %v", err) 81 } 82 }() 83 84 // Make sure it fetches when we clone again. 85 if err := lg.AddCommit("foo", "bar", map[string][]byte{"second": {}}); err != nil { 86 t.Fatalf("Adding second commit: %v", err) 87 } 88 r3, err := c.ClientFor("foo", "bar") 89 if err != nil { 90 t.Fatalf("Cloning a second time: %v", err) 91 } 92 defer func() { 93 if err := r3.Clean(); err != nil { 94 t.Errorf("Cleaning repo: %v", err) 95 } 96 }() 97 log := exec.Command("git", "log", "--oneline") 98 log.Dir = r3.Directory() 99 if b, err := log.CombinedOutput(); err != nil { 100 t.Fatalf("git log: %v, %s", err, string(b)) 101 } else { 102 t.Logf("git log output: %s", string(b)) 103 if len(bytes.Split(bytes.TrimSpace(b), []byte("\n"))) != 2 { 104 t.Error("Wrong number of commits in git log output. Expected 2") 105 } 106 } 107 } 108 109 func TestCheckoutPRV2(t *testing.T) { 110 testCheckoutPR(localgit.NewV2, t) 111 } 112 113 func testCheckoutPR(clients localgit.Clients, t *testing.T) { 114 lg, c, err := clients() 115 if err != nil { 116 t.Fatalf("Making local git repo: %v", err) 117 } 118 defer func() { 119 if err := lg.Clean(); err != nil { 120 t.Errorf("Error cleaning LocalGit: %v", err) 121 } 122 if err := c.Clean(); err != nil { 123 t.Errorf("Error cleaning Client: %v", err) 124 } 125 }() 126 if err := lg.MakeFakeRepo("foo", "bar"); err != nil { 127 t.Fatalf("Making fake repo: %v", err) 128 } 129 r, err := c.ClientFor("foo", "bar") 130 if err != nil { 131 t.Fatalf("Cloning: %v", err) 132 } 133 defer func() { 134 if err := r.Clean(); err != nil { 135 t.Errorf("Cleaning repo: %v", err) 136 } 137 }() 138 139 if err := lg.CheckoutNewBranch("foo", "bar", "pull/123/head"); err != nil { 140 t.Fatalf("Checkout new branch: %v", err) 141 } 142 if err := lg.AddCommit("foo", "bar", map[string][]byte{"wow": {}}); err != nil { 143 t.Fatalf("Add commit: %v", err) 144 } 145 146 if err := r.CheckoutPullRequest(123); err != nil { 147 t.Fatalf("Checking out PR: %v", err) 148 } 149 if _, err := os.Stat(filepath.Join(r.Directory(), "wow")); err != nil { 150 t.Errorf("Didn't find file in PR after checking out: %v", err) 151 } 152 } 153 154 func TestMergeCommitsExistBetweenV2(t *testing.T) { 155 testMergeCommitsExistBetween(localgit.NewV2, t) 156 } 157 158 func testMergeCommitsExistBetween(clients localgit.Clients, t *testing.T) { 159 lg, c, err := clients() 160 if err != nil { 161 t.Fatalf("Making local git repo: %v", err) 162 } 163 defer func() { 164 if err := lg.Clean(); err != nil { 165 t.Errorf("Cleaning up localgit: %v", err) 166 } 167 if err := c.Clean(); err != nil { 168 t.Errorf("Cleaning up client: %v", err) 169 } 170 }() 171 if err := lg.MakeFakeRepo("foo", "bar"); err != nil { 172 t.Fatalf("Making fake repo: %v", err) 173 } 174 r, err := c.ClientFor("foo", "bar") 175 if err != nil { 176 t.Fatalf("Cloning: %v", err) 177 } 178 defer func() { 179 if err := r.Clean(); err != nil { 180 t.Errorf("Cleaning repo: %v", err) 181 } 182 }() 183 var ( 184 checkoutPR = func(prNum int) { 185 if err := lg.CheckoutNewBranch("foo", "bar", fmt.Sprintf("pull/%d/head", prNum)); err != nil { 186 t.Fatalf("Creating & checking out pull branch pull/%d/head: %v", prNum, err) 187 } 188 } 189 checkoutBranch = func(branch string) { 190 if err := lg.Checkout("foo", "bar", branch); err != nil { 191 t.Fatalf("Checking out branch %s: %v", branch, err) 192 } 193 } 194 addCommit = func(file string) { 195 if err := lg.AddCommit("foo", "bar", map[string][]byte{file: {}}); err != nil { 196 t.Fatalf("Adding commit: %v", err) 197 } 198 } 199 mergeMaster = func() { 200 if _, err := lg.Merge("foo", "bar", defaultBranch); err != nil { 201 t.Fatalf("Rebasing commit: %v", err) 202 } 203 } 204 rebaseMaster = func() { 205 if _, err := lg.Rebase("foo", "bar", defaultBranch); err != nil { 206 t.Fatalf("Rebasing commit: %v", err) 207 } 208 } 209 ) 210 211 type testCase struct { 212 name string 213 prNum int 214 checkout func() 215 mergeOrRebase func() 216 checkoutPR func() error 217 want bool 218 } 219 testcases := []testCase{ 220 { 221 name: "PR has merge commits", 222 prNum: 1, 223 checkout: func() { checkoutBranch("pull/1/head") }, 224 mergeOrRebase: mergeMaster, 225 checkoutPR: func() error { return r.CheckoutPullRequest(1) }, 226 want: true, 227 }, 228 { 229 name: "PR doesn't have merge commits", 230 prNum: 2, 231 checkout: func() { checkoutBranch("pull/2/head") }, 232 mergeOrRebase: rebaseMaster, 233 checkoutPR: func() error { return r.CheckoutPullRequest(2) }, 234 want: false, 235 }, 236 } 237 238 addCommit("wow") 239 // preparation work: branch off all prs upon commit 'wow' 240 for _, tt := range testcases { 241 checkoutPR(tt.prNum) 242 } 243 // switch back to master and create a new commit 'ouch' 244 checkoutBranch(defaultBranch) 245 addCommit("ouch") 246 masterSHA, err := lg.RevParse("foo", "bar", "HEAD") 247 if err != nil { 248 t.Fatalf("Fetching SHA: %v", err) 249 } 250 251 for _, tt := range testcases { 252 tt.checkout() 253 tt.mergeOrRebase() 254 prSHA, err := lg.RevParse("foo", "bar", "HEAD") 255 if err != nil { 256 t.Fatalf("Fetching SHA: %v", err) 257 } 258 if err := tt.checkoutPR(); err != nil { 259 t.Fatalf("Checking out PR: %v", err) 260 } 261 // verify the content is up to dated 262 ouchPath := filepath.Join(r.Directory(), "ouch") 263 if _, err := os.Stat(ouchPath); err != nil { 264 t.Fatalf("Didn't find file 'ouch' in PR %d after merging: %v", tt.prNum, err) 265 } 266 267 got, err := r.MergeCommitsExistBetween(masterSHA, prSHA) 268 key := fmt.Sprintf("foo/bar/%d", tt.prNum) 269 if err != nil { 270 t.Errorf("Case: %v. Expect err is nil, but got %v", key, err) 271 } 272 if tt.want != got { 273 t.Errorf("Case: %v. Expect MergeCommitsExistBetween()=%v, but got %v", key, tt.want, got) 274 } 275 } 276 } 277 278 func TestMergeAndCheckoutV2(t *testing.T) { 279 testMergeAndCheckout(localgit.NewV2, t) 280 } 281 282 func testMergeAndCheckout(clients localgit.Clients, t *testing.T) { 283 testCases := []struct { 284 name string 285 setBaseSHA bool 286 prBranches []string 287 mergeStrategy types.PullRequestMergeType 288 err string 289 }{ 290 { 291 name: "Unset baseSHA, error", 292 err: "baseSHA must be set", 293 }, 294 { 295 name: "No mergeStrategy, error", 296 setBaseSHA: true, 297 prBranches: []string{"my-pr-branch"}, 298 err: "merge strategy \"\" is not supported", 299 }, 300 { 301 name: "Merge succeeds with rebase strategy", 302 setBaseSHA: true, 303 prBranches: []string{"my-pr-branch"}, 304 mergeStrategy: types.MergeRebase, 305 }, 306 { 307 name: "No pullRequestHead, no error", 308 setBaseSHA: true, 309 }, 310 { 311 name: "Merge succeeds with one head and merge strategy", 312 setBaseSHA: true, 313 prBranches: []string{"my-pr-branch"}, 314 mergeStrategy: types.MergeMerge, 315 }, 316 { 317 name: "Merge succeeds with multiple heads and merge strategy", 318 setBaseSHA: true, 319 prBranches: []string{"my-pr-branch", "my-other-pr-branch"}, 320 mergeStrategy: types.MergeMerge, 321 }, 322 { 323 name: "Merge succeeds with one head and squash strategy", 324 setBaseSHA: true, 325 prBranches: []string{"my-pr-branch"}, 326 mergeStrategy: types.MergeSquash, 327 }, 328 { 329 name: "Merge succeeds with multiple heads and squash stragey", 330 setBaseSHA: true, 331 prBranches: []string{"my-pr-branch", "my-other-pr-branch"}, 332 mergeStrategy: types.MergeSquash, 333 }, 334 } 335 336 const ( 337 org = "my-org" 338 repo = "my-repo" 339 ) 340 for _, tc := range testCases { 341 t.Run(tc.name, func(t *testing.T) { 342 tc := tc 343 t.Parallel() 344 345 lg, c, err := clients() 346 if err != nil { 347 t.Fatalf("Making local git repo: %v", err) 348 } 349 logrus.SetLevel(logrus.DebugLevel) 350 defer func() { 351 if err := lg.Clean(); err != nil { 352 t.Errorf("Error cleaning LocalGit: %v", err) 353 } 354 if err := c.Clean(); err != nil { 355 t.Errorf("Error cleaning Client: %v", err) 356 } 357 }() 358 if err := lg.MakeFakeRepo(org, repo); err != nil { 359 t.Fatalf("Making fake repo: %v", err) 360 } 361 362 var commitsToMerge []string 363 for _, prBranch := range tc.prBranches { 364 if err := lg.CheckoutNewBranch(org, repo, prBranch); err != nil { 365 t.Fatalf("failed to checkout new branch %q: %v", prBranch, err) 366 } 367 if err := lg.AddCommit(org, repo, map[string][]byte{prBranch: []byte("val")}); err != nil { 368 t.Fatalf("failed to add commit: %v", err) 369 } 370 headRef, err := lg.RevParse(org, repo, "HEAD") 371 if err != nil { 372 t.Fatalf("failed to run git rev-parse: %v", err) 373 } 374 commitsToMerge = append(commitsToMerge, headRef) 375 } 376 if len(tc.prBranches) > 0 { 377 if err := lg.Checkout(org, repo, defaultBranch); err != nil { 378 t.Fatalf("failed to run git checkout master: %v", err) 379 } 380 } 381 382 var baseSHA string 383 if tc.setBaseSHA { 384 baseSHA, err = lg.RevParse(org, repo, defaultBranch) 385 if err != nil { 386 t.Fatalf("failed to run git rev-parse master: %v", err) 387 } 388 } 389 390 clonedRepo, err := c.ClientFor(org, repo) 391 if err != nil { 392 t.Fatalf("Cloning failed: %v", err) 393 } 394 if err := clonedRepo.Config("user.name", "prow"); err != nil { 395 t.Fatalf("failed to set name for test repo: %v", err) 396 } 397 if err := clonedRepo.Config("user.email", "prow@localhost"); err != nil { 398 t.Fatalf("failed to set email for test repo: %v", err) 399 } 400 if err := clonedRepo.Config("commit.gpgsign", "false"); err != nil { 401 t.Fatalf("failed to disable gpg signing for test repo: %v", err) 402 } 403 404 err = clonedRepo.MergeAndCheckout(baseSHA, string(tc.mergeStrategy), commitsToMerge...) 405 if err == nil && tc.err == "" { 406 return 407 } 408 if err == nil || err.Error() != tc.err { 409 t.Errorf("Expected err %q but got \"%v\"", tc.err, err) 410 } 411 }) 412 } 413 414 } 415 416 func TestMergingV2(t *testing.T) { 417 testMerging(localgit.NewV2, t) 418 } 419 420 func testMerging(clients localgit.Clients, t *testing.T) { 421 testCases := []struct { 422 name string 423 strategy string 424 // branch -> filename -> content 425 branches map[string]map[string][]byte 426 mergeOrder []string 427 }{ 428 { 429 name: "Multiple branches, squash strategy", 430 strategy: "squash", 431 branches: map[string]map[string][]byte{ 432 "pr-1": {"file-1": []byte("some-content")}, 433 "pr-2": {"file-2": []byte("some-content")}, 434 }, 435 mergeOrder: []string{"pr-1", "pr-2"}, 436 }, 437 { 438 name: "Multiple branches, mergeMerge strategy", 439 strategy: "merge", 440 branches: map[string]map[string][]byte{ 441 "pr-1": {"file-1": []byte("some-content")}, 442 "pr-2": {"file-2": []byte("some-content")}, 443 }, 444 mergeOrder: []string{"pr-1", "pr-2"}, 445 }, 446 { 447 name: "Multiple branches, rebase strategy", 448 strategy: "rebase", 449 branches: map[string]map[string][]byte{ 450 "pr-1": {"file-1": []byte("some-content")}, 451 "pr-2": {"file-2": []byte("some-content")}, 452 }, 453 mergeOrder: []string{"pr-1", "pr-2"}, 454 }, 455 } 456 457 const org, repo = "org", "repo" 458 for _, tc := range testCases { 459 t.Run(tc.name, func(t *testing.T) { 460 tc := tc 461 t.Parallel() 462 463 lg, c, err := clients() 464 if err != nil { 465 t.Fatalf("Making local git repo: %v", err) 466 } 467 logrus.SetLevel(logrus.DebugLevel) 468 defer func() { 469 if err := lg.Clean(); err != nil { 470 t.Errorf("Error cleaning LocalGit: %v", err) 471 } 472 if err := c.Clean(); err != nil { 473 t.Errorf("Error cleaning Client: %v", err) 474 } 475 }() 476 if err := lg.MakeFakeRepo(org, repo); err != nil { 477 t.Fatalf("Making fake repo: %v", err) 478 } 479 baseSHA, err := lg.RevParse(org, repo, "HEAD") 480 if err != nil { 481 t.Fatalf("rev-parse HEAD: %v", err) 482 } 483 484 for branchName, branchContent := range tc.branches { 485 if err := lg.Checkout(org, repo, baseSHA); err != nil { 486 t.Fatalf("checkout baseSHA: %v", err) 487 } 488 if err := lg.CheckoutNewBranch(org, repo, branchName); err != nil { 489 t.Fatalf("checkout new branch: %v", err) 490 } 491 if err := lg.AddCommit(org, repo, branchContent); err != nil { 492 t.Fatalf("addCommit: %v", err) 493 } 494 } 495 496 if err := lg.Checkout(org, repo, baseSHA); err != nil { 497 t.Fatalf("checkout baseSHA: %v", err) 498 } 499 500 r, err := c.ClientFor(org, repo) 501 if err != nil { 502 t.Fatalf("clone: %v", err) 503 } 504 if err := r.Config("user.name", "prow"); err != nil { 505 t.Fatalf("config user.name: %v", err) 506 } 507 if err := r.Config("user.email", "prow@localhost"); err != nil { 508 t.Fatalf("config user.email: %v", err) 509 } 510 if err := r.Checkout(baseSHA); err != nil { 511 t.Fatalf("checkout baseSHA: %v", err) 512 } 513 514 for _, branch := range tc.mergeOrder { 515 if _, err := r.MergeWithStrategy("origin/"+branch, tc.strategy); err != nil { 516 t.Fatalf("mergeWithStrategy %s: %v", branch, err) 517 } 518 } 519 }) 520 } 521 } 522 523 func TestShowRefV2(t *testing.T) { 524 testShowRef(localgit.NewV2, t) 525 } 526 527 func testShowRef(clients localgit.Clients, t *testing.T) { 528 const org, repo = "org", "repo" 529 lg, c, err := clients() 530 if err != nil { 531 t.Fatalf("failed to get clients: %v", err) 532 } 533 defer func() { 534 if err := lg.Clean(); err != nil { 535 t.Errorf("Error cleaning LocalGit: %v", err) 536 } 537 if err := c.Clean(); err != nil { 538 t.Errorf("Error cleaning Client: %v", err) 539 } 540 }() 541 if err := lg.MakeFakeRepo(org, repo); err != nil { 542 t.Fatalf("Making fake repo: %v", err) 543 } 544 reference, err := lg.RevParse(org, repo, "HEAD") 545 if err != nil { 546 t.Fatalf("lg.RevParse: %v", err) 547 } 548 549 client, err := c.ClientFor(org, repo) 550 if err != nil { 551 t.Fatalf("clientFor: %v", err) 552 } 553 res, err := client.ShowRef("HEAD") 554 if err != nil { 555 t.Fatalf("ShowRef: %v", err) 556 } 557 if res != reference { 558 t.Errorf("expeted result to be %s, was %s", reference, res) 559 } 560 }