sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/pod-utils/clone/clone_test.go (about) 1 /* 2 Copyright 2018 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 package clone 18 19 import ( 20 "fmt" 21 "os" 22 "os/exec" 23 "reflect" 24 "testing" 25 "time" 26 27 prowapi "sigs.k8s.io/prow/pkg/apis/prowjobs/v1" 28 29 "github.com/google/go-cmp/cmp" 30 ) 31 32 func TestPathForRefs(t *testing.T) { 33 var testCases = []struct { 34 name string 35 refs prowapi.Refs 36 expected string 37 }{ 38 { 39 name: "literal override", 40 refs: prowapi.Refs{ 41 PathAlias: "alias", 42 }, 43 expected: "base/src/alias", 44 }, 45 { 46 name: "default generated", 47 refs: prowapi.Refs{ 48 Org: "org", 49 Repo: "repo", 50 }, 51 expected: "base/src/github.com/org/repo", 52 }, 53 } 54 55 for _, testCase := range testCases { 56 if actual, expected := PathForRefs("base", testCase.refs), testCase.expected; actual != expected { 57 t.Errorf("%s: expected path %q, got %q", testCase.name, expected, actual) 58 } 59 } 60 } 61 62 func boolPtr(v bool) *bool { 63 return &v 64 } 65 66 func TestCommandsForRefs(t *testing.T) { 67 fakeTimestamp := 100200300 68 var testCases = []struct { 69 name string 70 refs prowapi.Refs 71 dir, gitUserName, gitUserEmail, cookiePath string 72 env []string 73 expectedBase []runnable 74 expectedPull []runnable 75 authUser string 76 authToken string 77 }{ 78 { 79 name: "simplest case, minimal refs", 80 refs: prowapi.Refs{ 81 Org: "org", 82 Repo: "repo", 83 BaseRef: "master", 84 }, 85 dir: "/go", 86 expectedBase: []runnable{ 87 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 88 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 89 retryCommand{ 90 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}}, 91 fetchRetries, 92 }, 93 retryCommand{ 94 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}}, 95 fetchRetries, 96 }, 97 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 98 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 99 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 100 }, 101 expectedPull: []runnable{ 102 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 103 }, 104 }, 105 { 106 name: "simple case, root dir", 107 refs: prowapi.Refs{ 108 Org: "org", 109 Repo: "repo", 110 BaseRef: "master", 111 }, 112 dir: "/", 113 expectedBase: []runnable{ 114 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/src/github.com/org/repo"}}, 115 cloneCommand{dir: "/src/github.com/org/repo", command: "git", args: []string{"init"}}, 116 retryCommand{ 117 cloneCommand{dir: "/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}}, 118 fetchRetries, 119 }, 120 retryCommand{ 121 cloneCommand{dir: "/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}}, 122 fetchRetries, 123 }, 124 cloneCommand{dir: "/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 125 cloneCommand{dir: "/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 126 cloneCommand{dir: "/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 127 }, 128 expectedPull: []runnable{ 129 cloneCommand{dir: "/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 130 }, 131 }, 132 { 133 name: "minimal refs with git user name", 134 refs: prowapi.Refs{ 135 Org: "org", 136 Repo: "repo", 137 BaseRef: "master", 138 }, 139 gitUserName: "user", 140 dir: "/go", 141 expectedBase: []runnable{ 142 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 143 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 144 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"config", "user.name", "user"}}, 145 retryCommand{ 146 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}}, 147 fetchRetries, 148 }, 149 retryCommand{ 150 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}}, 151 fetchRetries, 152 }, 153 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 154 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 155 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 156 }, 157 expectedPull: []runnable{ 158 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 159 }, 160 }, 161 { 162 name: "minimal refs with git user email", 163 refs: prowapi.Refs{ 164 Org: "org", 165 Repo: "repo", 166 BaseRef: "master", 167 }, 168 gitUserEmail: "user@go.com", 169 dir: "/go", 170 expectedBase: []runnable{ 171 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 172 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 173 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"config", "user.email", "user@go.com"}}, 174 retryCommand{ 175 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}}, 176 fetchRetries, 177 }, 178 retryCommand{ 179 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}}, 180 fetchRetries, 181 }, 182 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 183 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 184 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 185 }, 186 expectedPull: []runnable{ 187 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 188 }, 189 }, 190 { 191 name: "minimal refs with http cookie file (skip submodules)", 192 refs: prowapi.Refs{ 193 Org: "org", 194 Repo: "repo", 195 BaseRef: "master", 196 SkipSubmodules: true, 197 }, 198 cookiePath: "/cookie.txt", 199 dir: "/go", 200 expectedBase: []runnable{ 201 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 202 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 203 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"config", "http.cookiefile", "/cookie.txt"}}, 204 retryCommand{ 205 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}}, 206 fetchRetries, 207 }, 208 retryCommand{ 209 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}}, 210 fetchRetries, 211 }, 212 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 213 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 214 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 215 }, 216 }, 217 { 218 name: "minimal refs with http cookie file", 219 refs: prowapi.Refs{ 220 Org: "org", 221 Repo: "repo", 222 BaseRef: "master", 223 }, 224 cookiePath: "/cookie.txt", 225 dir: "/go", 226 expectedBase: []runnable{ 227 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 228 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 229 retryCommand{ 230 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}}, 231 fetchRetries, 232 }, 233 retryCommand{ 234 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}}, 235 fetchRetries, 236 }, 237 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 238 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 239 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 240 }, 241 expectedPull: []runnable{ 242 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 243 }, 244 }, 245 { 246 name: "minimal refs with no submodules", 247 refs: prowapi.Refs{ 248 Org: "org", 249 Repo: "repo", 250 BaseRef: "master", 251 SkipSubmodules: true, 252 }, 253 dir: "/go", 254 expectedBase: []runnable{ 255 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 256 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 257 retryCommand{ 258 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}}, 259 fetchRetries, 260 }, 261 retryCommand{ 262 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}}, 263 fetchRetries, 264 }, 265 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 266 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 267 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 268 }, 269 expectedPull: nil, 270 }, 271 { 272 name: "minimal refs with oauth token", 273 authToken: "12345678", 274 refs: prowapi.Refs{ 275 Org: "org", 276 Repo: "repo", 277 BaseRef: "master", 278 }, 279 dir: "/go", 280 expectedBase: []runnable{ 281 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 282 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 283 retryCommand{ 284 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://12345678:x-oauth-basic@github.com/org/repo.git", "--tags", "--prune"}}, 285 fetchRetries, 286 }, 287 retryCommand{ 288 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://12345678:x-oauth-basic@github.com/org/repo.git", "master"}}, 289 fetchRetries, 290 }, 291 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 292 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 293 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 294 }, 295 expectedPull: []runnable{ 296 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 297 }, 298 }, 299 { 300 name: "minimal refs with GitHub App user and token", 301 authUser: "x-access-token", 302 authToken: "xxxxx", 303 refs: prowapi.Refs{ 304 Org: "org", 305 Repo: "repo", 306 BaseRef: "master", 307 }, 308 dir: "/go", 309 expectedBase: []runnable{ 310 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 311 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 312 retryCommand{ 313 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://x-access-token:xxxxx@github.com/org/repo.git", "--tags", "--prune"}}, 314 fetchRetries, 315 }, 316 retryCommand{ 317 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://x-access-token:xxxxx@github.com/org/repo.git", "master"}}, 318 fetchRetries, 319 }, 320 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 321 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 322 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 323 }, 324 expectedPull: []runnable{ 325 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 326 }, 327 }, 328 { 329 name: "minimal refs with GitHub App user and token and clone URI override", 330 authUser: "x-access-token", 331 authToken: "xxxxx", 332 refs: prowapi.Refs{ 333 Org: "org", 334 Repo: "repo", 335 BaseRef: "master", 336 CloneURI: "git@github.com:owner/repo", 337 }, 338 dir: "/go", 339 expectedBase: []runnable{ 340 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 341 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 342 retryCommand{ 343 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "git@github.com:owner/repo", "--tags", "--prune"}}, 344 fetchRetries, 345 }, 346 retryCommand{ 347 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "git@github.com:owner/repo", "master"}}, 348 fetchRetries, 349 }, 350 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 351 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 352 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 353 }, 354 expectedPull: []runnable{ 355 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 356 }, 357 }, 358 { 359 name: "refs with clone URI override", 360 refs: prowapi.Refs{ 361 Org: "org", 362 Repo: "repo", 363 BaseRef: "master", 364 CloneURI: "internet.com", 365 }, 366 dir: "/go", 367 expectedBase: []runnable{ 368 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 369 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 370 retryCommand{ 371 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "internet.com", "--tags", "--prune"}}, 372 fetchRetries, 373 }, 374 retryCommand{ 375 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "internet.com", "master"}}, 376 fetchRetries, 377 }, 378 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 379 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 380 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 381 }, 382 expectedPull: []runnable{ 383 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 384 }, 385 }, 386 { 387 name: "refs with clone URI override and oauth token specified", 388 authToken: "12345678", 389 refs: prowapi.Refs{ 390 Org: "org", 391 Repo: "repo", 392 BaseRef: "master", 393 CloneURI: "https://internet.com", 394 }, 395 dir: "/go", 396 expectedBase: []runnable{ 397 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 398 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 399 retryCommand{ 400 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://12345678:x-oauth-basic@internet.com", "--tags", "--prune"}}, 401 fetchRetries, 402 }, 403 retryCommand{ 404 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://12345678:x-oauth-basic@internet.com", "master"}}, 405 fetchRetries, 406 }, 407 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 408 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 409 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 410 }, 411 expectedPull: []runnable{ 412 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 413 }, 414 }, 415 { 416 name: "refs with path alias", 417 refs: prowapi.Refs{ 418 Org: "org", 419 Repo: "repo", 420 BaseRef: "master", 421 PathAlias: "my/favorite/dir", 422 RepoLink: "https://github.com/org/repo", 423 }, 424 dir: "/go", 425 expectedBase: []runnable{ 426 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/my/favorite/dir"}}, 427 cloneCommand{dir: "/go/src/my/favorite/dir", command: "git", args: []string{"init"}}, 428 retryCommand{ 429 cloneCommand{dir: "/go/src/my/favorite/dir", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}}, 430 fetchRetries, 431 }, 432 retryCommand{ 433 cloneCommand{dir: "/go/src/my/favorite/dir", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}}, 434 fetchRetries, 435 }, 436 cloneCommand{dir: "/go/src/my/favorite/dir", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 437 cloneCommand{dir: "/go/src/my/favorite/dir", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 438 cloneCommand{dir: "/go/src/my/favorite/dir", command: "git", args: []string{"checkout", "master"}}, 439 }, 440 expectedPull: []runnable{ 441 cloneCommand{dir: "/go/src/my/favorite/dir", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 442 }, 443 }, 444 { 445 name: "refs with specific base sha", 446 refs: prowapi.Refs{ 447 Org: "org", 448 Repo: "repo", 449 BaseRef: "master", 450 BaseSHA: "abcdef", 451 }, 452 dir: "/go", 453 expectedBase: []runnable{ 454 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 455 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 456 retryCommand{ 457 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}}, 458 fetchRetries, 459 }, 460 retryCommand{ 461 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "abcdef"}}, 462 fetchRetries, 463 }, 464 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "abcdef"}}, 465 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "abcdef"}}, 466 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 467 }, 468 expectedPull: []runnable{ 469 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 470 }, 471 }, 472 { 473 name: "refs with simple pr ref", 474 refs: prowapi.Refs{ 475 Org: "org", 476 Repo: "repo", 477 BaseRef: "master", 478 Pulls: []prowapi.Pull{ 479 {Number: 1}, 480 }, 481 }, 482 dir: "/go", 483 expectedBase: []runnable{ 484 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 485 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 486 retryCommand{ 487 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}}, 488 fetchRetries, 489 }, 490 retryCommand{ 491 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}}, 492 fetchRetries, 493 }, 494 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 495 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 496 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 497 }, 498 expectedPull: []runnable{ 499 retryCommand{ 500 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "pull/1/head"}}, 501 fetchRetries, 502 }, 503 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"merge", "--no-ff", "FETCH_HEAD"}, env: gitTimestampEnvs(fakeTimestamp + 1)}, 504 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 505 }, 506 }, 507 { 508 name: "refs with simple pr ref, sha takes precedence over virtual pull ref", 509 refs: prowapi.Refs{ 510 Org: "org", 511 Repo: "repo", 512 BaseRef: "master", 513 Pulls: []prowapi.Pull{ 514 {Number: 1, SHA: "pull-1-sha"}, 515 }, 516 }, 517 dir: "/go", 518 expectedBase: []runnable{ 519 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 520 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 521 retryCommand{ 522 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}}, 523 fetchRetries, 524 }, 525 retryCommand{ 526 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}}, 527 fetchRetries, 528 }, 529 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 530 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 531 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 532 }, 533 expectedPull: []runnable{ 534 retryCommand{ 535 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "pull-1-sha"}}, 536 fetchRetries, 537 }, 538 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"merge", "--no-ff", "pull-1-sha"}, env: gitTimestampEnvs(fakeTimestamp + 1)}, 539 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 540 }, 541 }, 542 { 543 name: "refs with pr ref override", 544 refs: prowapi.Refs{ 545 Org: "org", 546 Repo: "repo", 547 BaseRef: "master", 548 Pulls: []prowapi.Pull{ 549 {Number: 1, Ref: "pull-me"}, 550 }, 551 }, 552 dir: "/go", 553 expectedBase: []runnable{ 554 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 555 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 556 retryCommand{ 557 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}}, 558 fetchRetries, 559 }, 560 retryCommand{ 561 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}}, 562 fetchRetries, 563 }, 564 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 565 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 566 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 567 }, 568 expectedPull: []runnable{ 569 retryCommand{ 570 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "pull-me"}}, 571 fetchRetries, 572 }, 573 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"merge", "--no-ff", "FETCH_HEAD"}, env: gitTimestampEnvs(fakeTimestamp + 1)}, 574 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 575 }, 576 }, 577 { 578 name: "blobless refs with pr ref override", 579 refs: prowapi.Refs{ 580 Org: "org", 581 Repo: "repo", 582 BaseRef: "master", 583 Pulls: []prowapi.Pull{ 584 {Number: 1, Ref: "pull-me"}, 585 }, 586 BloblessFetch: boolPtr(true), 587 }, 588 dir: "/go", 589 expectedBase: []runnable{ 590 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 591 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 592 retryCommand{ 593 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "--filter=blob:none", "https://github.com/org/repo.git", "--tags", "--prune"}}, 594 fetchRetries, 595 }, 596 retryCommand{ 597 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "--filter=blob:none", "https://github.com/org/repo.git", "master"}}, 598 fetchRetries, 599 }, 600 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 601 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 602 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 603 }, 604 expectedPull: []runnable{ 605 retryCommand{ 606 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "--filter=blob:none", "https://github.com/org/repo.git", "pull-me"}}, 607 fetchRetries, 608 }, 609 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"merge", "--no-ff", "FETCH_HEAD"}, env: gitTimestampEnvs(fakeTimestamp + 1)}, 610 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 611 }, 612 }, 613 { 614 name: "refs with pr ref with specific sha", 615 refs: prowapi.Refs{ 616 Org: "org", 617 Repo: "repo", 618 BaseRef: "master", 619 Pulls: []prowapi.Pull{ 620 {Number: 1, SHA: "abcdef"}, 621 }, 622 }, 623 dir: "/go", 624 expectedBase: []runnable{ 625 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 626 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 627 retryCommand{ 628 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}}, 629 fetchRetries, 630 }, 631 retryCommand{ 632 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}}, 633 fetchRetries, 634 }, 635 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 636 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 637 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 638 }, 639 expectedPull: []runnable{ 640 retryCommand{ 641 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "abcdef"}}, 642 fetchRetries, 643 }, 644 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"merge", "--no-ff", "abcdef"}, env: gitTimestampEnvs(fakeTimestamp + 1)}, 645 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 646 }, 647 }, 648 { 649 name: "refs with multiple simple pr refs", 650 refs: prowapi.Refs{ 651 Org: "org", 652 Repo: "repo", 653 BaseRef: "master", 654 Pulls: []prowapi.Pull{ 655 {Number: 1}, 656 {Number: 2}, 657 }, 658 }, 659 dir: "/go", 660 expectedBase: []runnable{ 661 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.com/org/repo"}}, 662 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"init"}}, 663 retryCommand{ 664 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "--tags", "--prune"}}, 665 fetchRetries, 666 }, 667 retryCommand{ 668 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "master"}}, 669 fetchRetries, 670 }, 671 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "FETCH_HEAD"}}, 672 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "FETCH_HEAD"}}, 673 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 674 }, 675 expectedPull: []runnable{ 676 retryCommand{ 677 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "pull/1/head"}}, 678 fetchRetries, 679 }, 680 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"merge", "--no-ff", "FETCH_HEAD"}, env: gitTimestampEnvs(fakeTimestamp + 1)}, 681 retryCommand{ 682 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"fetch", "https://github.com/org/repo.git", "pull/2/head"}}, 683 fetchRetries, 684 }, 685 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"merge", "--no-ff", "FETCH_HEAD"}, env: gitTimestampEnvs(fakeTimestamp + 2)}, 686 cloneCommand{dir: "/go/src/github.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 687 }, 688 }, 689 { 690 name: "refs with repo link", 691 refs: prowapi.Refs{ 692 Org: "org", 693 Repo: "repo", 694 BaseRef: "master", 695 BaseSHA: "abcdef", 696 RepoLink: "https://github.enterprise.com/org/repo", 697 }, 698 dir: "/go", 699 expectedBase: []runnable{ 700 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.enterprise.com/org/repo"}}, 701 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"init"}}, 702 retryCommand{ 703 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"fetch", "https://github.enterprise.com/org/repo.git", "--tags", "--prune"}}, 704 fetchRetries, 705 }, 706 retryCommand{ 707 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"fetch", "https://github.enterprise.com/org/repo.git", "abcdef"}}, 708 fetchRetries, 709 }, 710 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"checkout", "abcdef"}}, 711 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "abcdef"}}, 712 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 713 }, 714 expectedPull: []runnable{ 715 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 716 }, 717 }, 718 { 719 name: "support fetching repo with multiple heads", 720 refs: prowapi.Refs{ 721 Org: "org", 722 Repo: "repo", 723 BaseRef: "master", 724 BaseSHA: "abcdef", 725 RepoLink: "https://github.enterprise.com/org/repo", 726 SkipFetchHead: true, // no single HEAD 727 }, 728 dir: "/go", 729 expectedBase: []runnable{ 730 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.enterprise.com/org/repo"}}, 731 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"init"}}, 732 retryCommand{ 733 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"fetch", "https://github.enterprise.com/org/repo.git", "abcdef"}}, 734 fetchRetries, 735 }, 736 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"checkout", "abcdef"}}, 737 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "abcdef"}}, 738 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 739 }, 740 expectedPull: []runnable{ 741 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 742 }, 743 }, 744 { 745 name: "support shallow fetching repo with multiple heads", 746 refs: prowapi.Refs{ 747 Org: "org", 748 Repo: "repo", 749 BaseRef: "master", 750 BaseSHA: "abcdef", 751 RepoLink: "https://github.enterprise.com/org/repo", 752 SkipFetchHead: true, // no single HEAD 753 CloneDepth: 2, 754 }, 755 dir: "/go", 756 expectedBase: []runnable{ 757 cloneCommand{dir: "/", command: "mkdir", args: []string{"-p", "/go/src/github.enterprise.com/org/repo"}}, 758 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"init"}}, 759 retryCommand{ 760 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"fetch", "--depth", "2", "https://github.enterprise.com/org/repo.git", "abcdef"}}, 761 fetchRetries, 762 }, 763 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"checkout", "abcdef"}}, 764 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"branch", "--force", "master", "abcdef"}}, 765 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"checkout", "master"}}, 766 }, 767 expectedPull: []runnable{ 768 cloneCommand{dir: "/go/src/github.enterprise.com/org/repo", command: "git", args: []string{"submodule", "update", "--init", "--recursive"}}, 769 }, 770 }, 771 } 772 773 allow := cmp.AllowUnexported(retryCommand{}, cloneCommand{}) 774 for _, testCase := range testCases { 775 t.Run(testCase.name, func(t *testing.T) { 776 g := gitCtxForRefs(testCase.refs, testCase.dir, testCase.env, testCase.authUser, testCase.authToken) 777 actualBase := g.commandsForBaseRef(testCase.refs, testCase.gitUserName, testCase.gitUserEmail, testCase.cookiePath) 778 if diff := cmp.Diff(actualBase, testCase.expectedBase, allow); diff != "" { 779 t.Errorf("commandsForBaseRef() got unexpected diff (-got, +want):\n%s", diff) 780 } 781 782 actualPull := g.commandsForPullRefs(testCase.refs, fakeTimestamp) 783 if diff := cmp.Diff(actualPull, testCase.expectedPull, allow); diff != "" { 784 t.Errorf("commandsForPullRefs() got unexpected diff (-got, +want):\n%s", diff) 785 } 786 }) 787 } 788 } 789 790 func TestGitHeadTimestamp(t *testing.T) { 791 fakeTimestamp := 987654321 792 fakeGitDir, err := makeFakeGitRepo(t, fakeTimestamp) 793 if err != nil { 794 t.Errorf("error creating fake git dir: %v", err) 795 } 796 797 var testCases = []struct { 798 name string 799 dir string 800 noPath bool 801 expected int 802 expectError bool 803 }{ 804 { 805 name: "root - no git", 806 dir: "/", 807 expected: 0, 808 expectError: true, 809 }, 810 { 811 name: "fake git repo", 812 dir: fakeGitDir, 813 expected: fakeTimestamp, 814 expectError: false, 815 }, 816 { 817 name: "fake git repo but no git binary", 818 dir: fakeGitDir, 819 noPath: true, 820 expected: 0, 821 expectError: true, 822 }, 823 } 824 origCwd, err := os.Getwd() 825 if err != nil { 826 t.Errorf("failed getting cwd: %v", err) 827 } 828 origPath := os.Getenv("PATH") 829 for _, testCase := range testCases { 830 t.Run(testCase.name, func(t *testing.T) { 831 if err := os.Chdir(testCase.dir); err != nil { 832 t.Errorf("%s: failed to chdir to %s: %v", testCase.name, testCase.dir, err) 833 } 834 if testCase.noPath { 835 if err := os.Unsetenv("PATH"); err != nil { 836 t.Errorf("%s: failed to unset PATH: %v", testCase.name, err) 837 } 838 } 839 g := gitCtx{ 840 cloneDir: testCase.dir, 841 } 842 timestamp, err := g.gitHeadTimestamp() 843 if timestamp != testCase.expected { 844 t.Errorf("%s: timestamp %d does not match expected timestamp %d", testCase.name, timestamp, testCase.expected) 845 } 846 if (err == nil && testCase.expectError) || (err != nil && !testCase.expectError) { 847 t.Errorf("%s: expect error is %v but received error %v", testCase.name, testCase.expectError, err) 848 } 849 if err := os.Chdir(origCwd); err != nil { 850 t.Errorf("%s: failed to chdir to original cwd %s: %v", testCase.name, origCwd, err) 851 } 852 if testCase.noPath { 853 if err := os.Setenv("PATH", origPath); err != nil { 854 t.Errorf("%s: failed to set PATH to original: %v", testCase.name, err) 855 } 856 } 857 }) 858 } 859 } 860 861 // makeFakeGitRepo creates a fake git repo with a constant digest and timestamp. 862 func makeFakeGitRepo(t *testing.T, fakeTimestamp int) (string, error) { 863 fakeGitDir := t.TempDir() 864 cmds := [][]string{ 865 {"git", "init"}, 866 {"git", "config", "user.email", "test@test.test"}, 867 {"git", "config", "user.name", "test test"}, 868 {"touch", "a_file"}, 869 {"git", "add", "a_file"}, 870 {"git", "commit", "-m", "adding a_file"}, 871 } 872 for _, cmd := range cmds { 873 c := exec.Command(cmd[0], cmd[1:]...) 874 c.Dir = fakeGitDir 875 c.Env = append(os.Environ(), gitTimestampEnvs(fakeTimestamp)...) 876 if err := c.Run(); err != nil { 877 return fakeGitDir, err 878 } 879 } 880 return fakeGitDir, nil 881 } 882 883 func TestCensorToken(t *testing.T) { 884 testCases := []struct { 885 id string 886 token string 887 msg string 888 expected string 889 }{ 890 { 891 id: "no token", 892 msg: "git fetch https://github.com/kubernetes/test-infra.git", 893 expected: "git fetch https://github.com/kubernetes/test-infra.git", 894 }, 895 { 896 id: "with token", 897 token: "123456789", 898 msg: "git fetch 123456789:x-oauth-basic@https://github.com/kubernetes/test-infra.git", 899 expected: "git fetch CENSORED:x-oauth-basic@https://github.com/kubernetes/test-infra.git", 900 }, 901 { 902 id: "git output with token", 903 token: "123456789", 904 msg: ` 905 Cloning into 'test-infa'... 906 remote: Invalid username or password. 907 fatal: Authentication failed for 'https://123456789@github.com/kubernetes/test-infa/' 908 `, 909 expected: ` 910 Cloning into 'test-infa'... 911 remote: Invalid username or password. 912 fatal: Authentication failed for 'https://CENSORED@github.com/kubernetes/test-infa/' 913 `, 914 }, 915 } 916 917 for _, tc := range testCases { 918 t.Run(tc.id, func(t *testing.T) { 919 censoredMsg := censorToken(tc.msg, tc.token) 920 if !reflect.DeepEqual(censoredMsg, tc.expected) { 921 t.Fatalf("expected: %s got %s", tc.expected, censoredMsg) 922 } 923 }) 924 } 925 } 926 927 // fakeRunner will pass run() if called when calls == 1, 928 // decrementing calls each time. 929 type fakeRunner struct { 930 calls int 931 } 932 933 func (fr *fakeRunner) run() (string, string, error) { 934 fr.calls-- 935 if fr.calls == 0 { 936 return "command", "output", nil 937 } 938 return "command", "output", fmt.Errorf("calls: %d", fr.calls) 939 } 940 941 func TestGitFetch(t *testing.T) { 942 const short = time.Nanosecond 943 command := func(calls int, retries ...time.Duration) retryCommand { 944 return retryCommand{ 945 runnable: &fakeRunner{calls}, 946 retries: retries, 947 } 948 } 949 cases := []struct { 950 name string 951 retryCommand 952 err bool 953 }{ 954 { 955 name: "works without retires", 956 retryCommand: command(1), 957 }, 958 { 959 name: "errors if first call fails without retries", 960 retryCommand: command(0), 961 err: true, 962 }, 963 { 964 name: "works with retries (without retrying)", 965 retryCommand: command(1, short), 966 }, 967 { 968 name: "works with retries (retrying)", 969 retryCommand: command(2, short), 970 }, 971 { 972 name: "errors without retries if first call fails", 973 retryCommand: command(2), 974 err: true, 975 }, 976 { 977 name: "errors with retries when all retries are consumed", 978 retryCommand: command(3, short), 979 err: true, 980 }, 981 } 982 983 for _, tc := range cases { 984 t.Run(tc.name, func(t *testing.T) { 985 _, _, err := tc.run() 986 switch { 987 case err != nil: 988 if !tc.err { 989 t.Errorf("unexpected error: %v", err) 990 } 991 case tc.err: 992 t.Error("failed to received expected error") 993 } 994 }) 995 } 996 } 997 998 func TestCloneCommandString(t *testing.T) { 999 tests := []struct { 1000 name string 1001 cc cloneCommand 1002 want string 1003 }{ 1004 { 1005 name: "empty", 1006 cc: cloneCommand{}, 1007 want: "PWD= ", 1008 }, 1009 { 1010 name: "base", 1011 cc: cloneCommand{ 1012 dir: "abc", 1013 env: []string{"d=e", "f=g"}, 1014 command: "echo", 1015 args: []string{"hij klm"}, 1016 }, 1017 want: "PWD=abc d=e f=g echo hij klm", 1018 }, 1019 } 1020 1021 for _, tc := range tests { 1022 tc := tc 1023 t.Run(tc.name, func(t *testing.T) { 1024 want, got := tc.want, tc.cc.String() 1025 if diff := cmp.Diff(want, got); diff != "" { 1026 t.Errorf("mismatch. want(-), got(+):\n%s", diff) 1027 } 1028 }) 1029 } 1030 }