github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/go/vcs/vcs_test.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package vcs 6 7 import ( 8 "errors" 9 "fmt" 10 "os" 11 "path/filepath" 12 "strings" 13 "testing" 14 15 "github.com/go-asm/go/testenv" 16 17 "github.com/go-asm/go/cmd/go/web" 18 ) 19 20 func init() { 21 // GOVCS defaults to public:git|hg,private:all, 22 // which breaks many tests here - they can't use non-git, non-hg VCS at all! 23 // Change to fully permissive. 24 // The tests of the GOVCS setting itself are in ../../testdata/script/govcs.txt. 25 os.Setenv("GOVCS", "*:all") 26 } 27 28 // Test that RepoRootForImportPath determines the correct RepoRoot for a given importPath. 29 // TODO(cmang): Add tests for SVN and BZR. 30 func TestRepoRootForImportPath(t *testing.T) { 31 testenv.MustHaveExternalNetwork(t) 32 33 tests := []struct { 34 path string 35 want *RepoRoot 36 }{ 37 { 38 "github.com/golang/groupcache", 39 &RepoRoot{ 40 VCS: vcsGit, 41 Repo: "https://github.com/golang/groupcache", 42 }, 43 }, 44 // Unicode letters in directories are not valid. 45 { 46 "github.com/user/unicode/испытание", 47 nil, 48 }, 49 // IBM DevOps Services tests 50 { 51 "hub.jazz.net/git/user1/pkgname", 52 &RepoRoot{ 53 VCS: vcsGit, 54 Repo: "https://hub.jazz.net/git/user1/pkgname", 55 }, 56 }, 57 { 58 "hub.jazz.net/git/user1/pkgname/submodule/submodule/submodule", 59 &RepoRoot{ 60 VCS: vcsGit, 61 Repo: "https://hub.jazz.net/git/user1/pkgname", 62 }, 63 }, 64 { 65 "hub.jazz.net", 66 nil, 67 }, 68 { 69 "hubajazz.net", 70 nil, 71 }, 72 { 73 "hub2.jazz.net", 74 nil, 75 }, 76 { 77 "hub.jazz.net/someotherprefix", 78 nil, 79 }, 80 { 81 "hub.jazz.net/someotherprefix/user1/pkgname", 82 nil, 83 }, 84 // Spaces are not valid in user names or package names 85 { 86 "hub.jazz.net/git/User 1/pkgname", 87 nil, 88 }, 89 { 90 "hub.jazz.net/git/user1/pkg name", 91 nil, 92 }, 93 // Dots are not valid in user names 94 { 95 "hub.jazz.net/git/user.1/pkgname", 96 nil, 97 }, 98 { 99 "hub.jazz.net/git/user/pkg.name", 100 &RepoRoot{ 101 VCS: vcsGit, 102 Repo: "https://hub.jazz.net/git/user/pkg.name", 103 }, 104 }, 105 // User names cannot have uppercase letters 106 { 107 "hub.jazz.net/git/USER/pkgname", 108 nil, 109 }, 110 // OpenStack tests 111 { 112 "git.openstack.org/openstack/swift", 113 &RepoRoot{ 114 VCS: vcsGit, 115 Repo: "https://git.openstack.org/openstack/swift", 116 }, 117 }, 118 // Trailing .git is less preferred but included for 119 // compatibility purposes while the same source needs to 120 // be compilable on both old and new go 121 { 122 "git.openstack.org/openstack/swift.git", 123 &RepoRoot{ 124 VCS: vcsGit, 125 Repo: "https://git.openstack.org/openstack/swift.git", 126 }, 127 }, 128 { 129 "git.openstack.org/openstack/swift/go/hummingbird", 130 &RepoRoot{ 131 VCS: vcsGit, 132 Repo: "https://git.openstack.org/openstack/swift", 133 }, 134 }, 135 { 136 "git.openstack.org", 137 nil, 138 }, 139 { 140 "git.openstack.org/openstack", 141 nil, 142 }, 143 // Spaces are not valid in package name 144 { 145 "git.apache.org/package name/path/to/lib", 146 nil, 147 }, 148 // Should have ".git" suffix 149 { 150 "git.apache.org/package-name/path/to/lib", 151 nil, 152 }, 153 { 154 "gitbapache.org", 155 nil, 156 }, 157 { 158 "git.apache.org/package-name.git", 159 &RepoRoot{ 160 VCS: vcsGit, 161 Repo: "https://git.apache.org/package-name.git", 162 }, 163 }, 164 { 165 "git.apache.org/package-name_2.x.git/path/to/lib", 166 &RepoRoot{ 167 VCS: vcsGit, 168 Repo: "https://git.apache.org/package-name_2.x.git", 169 }, 170 }, 171 { 172 "chiselapp.com/user/kyle/repository/fossilgg", 173 &RepoRoot{ 174 VCS: vcsFossil, 175 Repo: "https://chiselapp.com/user/kyle/repository/fossilgg", 176 }, 177 }, 178 { 179 // must have a user/$name/repository/$repo path 180 "chiselapp.com/kyle/repository/fossilgg", 181 nil, 182 }, 183 { 184 "chiselapp.com/user/kyle/fossilgg", 185 nil, 186 }, 187 { 188 "bitbucket.org/workspace/pkgname", 189 &RepoRoot{ 190 VCS: vcsGit, 191 Repo: "https://bitbucket.org/workspace/pkgname", 192 }, 193 }, 194 } 195 196 for _, test := range tests { 197 got, err := RepoRootForImportPath(test.path, IgnoreMod, web.SecureOnly) 198 want := test.want 199 200 if want == nil { 201 if err == nil { 202 t.Errorf("RepoRootForImportPath(%q): Error expected but not received", test.path) 203 } 204 continue 205 } 206 if err != nil { 207 t.Errorf("RepoRootForImportPath(%q): %v", test.path, err) 208 continue 209 } 210 if got.VCS.Name != want.VCS.Name || got.Repo != want.Repo { 211 t.Errorf("RepoRootForImportPath(%q) = VCS(%s) Repo(%s), want VCS(%s) Repo(%s)", test.path, got.VCS, got.Repo, want.VCS, want.Repo) 212 } 213 } 214 } 215 216 // Test that vcs.FromDir correctly inspects a given directory and returns the 217 // right VCS and repo directory. 218 func TestFromDir(t *testing.T) { 219 tempDir := t.TempDir() 220 221 for _, vcs := range vcsList { 222 for r, root := range vcs.RootNames { 223 vcsName := fmt.Sprint(vcs.Name, r) 224 dir := filepath.Join(tempDir, "example.com", vcsName, root.filename) 225 if root.isDir { 226 err := os.MkdirAll(dir, 0755) 227 if err != nil { 228 t.Fatal(err) 229 } 230 } else { 231 err := os.MkdirAll(filepath.Dir(dir), 0755) 232 if err != nil { 233 t.Fatal(err) 234 } 235 f, err := os.Create(dir) 236 if err != nil { 237 t.Fatal(err) 238 } 239 f.Close() 240 } 241 242 wantRepoDir := filepath.Dir(dir) 243 gotRepoDir, gotVCS, err := FromDir(dir, tempDir, false) 244 if err != nil { 245 t.Errorf("FromDir(%q, %q): %v", dir, tempDir, err) 246 continue 247 } 248 if gotRepoDir != wantRepoDir || gotVCS.Name != vcs.Name { 249 t.Errorf("FromDir(%q, %q) = RepoDir(%s), VCS(%s); want RepoDir(%s), VCS(%s)", dir, tempDir, gotRepoDir, gotVCS.Name, wantRepoDir, vcs.Name) 250 } 251 } 252 } 253 } 254 255 func TestIsSecure(t *testing.T) { 256 tests := []struct { 257 vcs *Cmd 258 url string 259 secure bool 260 }{ 261 {vcsGit, "http://example.com/foo.git", false}, 262 {vcsGit, "https://example.com/foo.git", true}, 263 {vcsBzr, "http://example.com/foo.bzr", false}, 264 {vcsBzr, "https://example.com/foo.bzr", true}, 265 {vcsSvn, "http://example.com/svn", false}, 266 {vcsSvn, "https://example.com/svn", true}, 267 {vcsHg, "http://example.com/foo.hg", false}, 268 {vcsHg, "https://example.com/foo.hg", true}, 269 {vcsGit, "ssh://user@example.com/foo.git", true}, 270 {vcsGit, "user@server:path/to/repo.git", false}, 271 {vcsGit, "user@server:", false}, 272 {vcsGit, "server:repo.git", false}, 273 {vcsGit, "server:path/to/repo.git", false}, 274 {vcsGit, "example.com:path/to/repo.git", false}, 275 {vcsGit, "path/that/contains/a:colon/repo.git", false}, 276 {vcsHg, "ssh://user@example.com/path/to/repo.hg", true}, 277 {vcsFossil, "http://example.com/foo", false}, 278 {vcsFossil, "https://example.com/foo", true}, 279 } 280 281 for _, test := range tests { 282 secure := test.vcs.IsSecure(test.url) 283 if secure != test.secure { 284 t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure) 285 } 286 } 287 } 288 289 func TestIsSecureGitAllowProtocol(t *testing.T) { 290 tests := []struct { 291 vcs *Cmd 292 url string 293 secure bool 294 }{ 295 // Same as TestIsSecure to verify same behavior. 296 {vcsGit, "http://example.com/foo.git", false}, 297 {vcsGit, "https://example.com/foo.git", true}, 298 {vcsBzr, "http://example.com/foo.bzr", false}, 299 {vcsBzr, "https://example.com/foo.bzr", true}, 300 {vcsSvn, "http://example.com/svn", false}, 301 {vcsSvn, "https://example.com/svn", true}, 302 {vcsHg, "http://example.com/foo.hg", false}, 303 {vcsHg, "https://example.com/foo.hg", true}, 304 {vcsGit, "user@server:path/to/repo.git", false}, 305 {vcsGit, "user@server:", false}, 306 {vcsGit, "server:repo.git", false}, 307 {vcsGit, "server:path/to/repo.git", false}, 308 {vcsGit, "example.com:path/to/repo.git", false}, 309 {vcsGit, "path/that/contains/a:colon/repo.git", false}, 310 {vcsHg, "ssh://user@example.com/path/to/repo.hg", true}, 311 // New behavior. 312 {vcsGit, "ssh://user@example.com/foo.git", false}, 313 {vcsGit, "foo://example.com/bar.git", true}, 314 {vcsHg, "foo://example.com/bar.hg", false}, 315 {vcsSvn, "foo://example.com/svn", false}, 316 {vcsBzr, "foo://example.com/bar.bzr", false}, 317 } 318 319 defer os.Unsetenv("GIT_ALLOW_PROTOCOL") 320 os.Setenv("GIT_ALLOW_PROTOCOL", "https:foo") 321 for _, test := range tests { 322 secure := test.vcs.IsSecure(test.url) 323 if secure != test.secure { 324 t.Errorf("%s isSecure(%q) = %t; want %t", test.vcs, test.url, secure, test.secure) 325 } 326 } 327 } 328 329 func TestMatchGoImport(t *testing.T) { 330 tests := []struct { 331 imports []metaImport 332 path string 333 mi metaImport 334 err error 335 }{ 336 { 337 imports: []metaImport{ 338 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 339 }, 340 path: "example.com/user/foo", 341 mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 342 }, 343 { 344 imports: []metaImport{ 345 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 346 }, 347 path: "example.com/user/foo/", 348 mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 349 }, 350 { 351 imports: []metaImport{ 352 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 353 {Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 354 }, 355 path: "example.com/user/foo", 356 mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 357 }, 358 { 359 imports: []metaImport{ 360 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 361 {Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 362 }, 363 path: "example.com/user/fooa", 364 mi: metaImport{Prefix: "example.com/user/fooa", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 365 }, 366 { 367 imports: []metaImport{ 368 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 369 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 370 }, 371 path: "example.com/user/foo/bar", 372 err: errors.New("should not be allowed to create nested repo"), 373 }, 374 { 375 imports: []metaImport{ 376 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 377 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 378 }, 379 path: "example.com/user/foo/bar/baz", 380 err: errors.New("should not be allowed to create nested repo"), 381 }, 382 { 383 imports: []metaImport{ 384 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 385 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 386 }, 387 path: "example.com/user/foo/bar/baz/qux", 388 err: errors.New("should not be allowed to create nested repo"), 389 }, 390 { 391 imports: []metaImport{ 392 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 393 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 394 }, 395 path: "example.com/user/foo/bar/baz/", 396 err: errors.New("should not be allowed to create nested repo"), 397 }, 398 { 399 imports: []metaImport{ 400 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 401 {Prefix: "example.com/user/foo/bar", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 402 }, 403 path: "example.com", 404 err: errors.New("pathologically short path"), 405 }, 406 { 407 imports: []metaImport{ 408 {Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target"}, 409 }, 410 path: "different.example.com/user/foo", 411 err: errors.New("meta tags do not match import path"), 412 }, 413 { 414 imports: []metaImport{ 415 {Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"}, 416 {Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"}, 417 }, 418 path: "myitcv.io/blah2/foo", 419 mi: metaImport{Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"}, 420 }, 421 { 422 imports: []metaImport{ 423 {Prefix: "myitcv.io/blah2", VCS: "mod", RepoRoot: "https://raw.githubusercontent.com/myitcv/pubx/master"}, 424 {Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"}, 425 }, 426 path: "myitcv.io/other", 427 mi: metaImport{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"}, 428 }, 429 } 430 431 for _, test := range tests { 432 mi, err := matchGoImport(test.imports, test.path) 433 if mi != test.mi { 434 t.Errorf("unexpected metaImport; got %v, want %v", mi, test.mi) 435 } 436 437 got := err 438 want := test.err 439 if (got == nil) != (want == nil) { 440 t.Errorf("unexpected error; got %v, want %v", got, want) 441 } 442 } 443 } 444 445 func TestValidateRepoRoot(t *testing.T) { 446 tests := []struct { 447 root string 448 ok bool 449 }{ 450 { 451 root: "", 452 ok: false, 453 }, 454 { 455 root: "http://", 456 ok: true, 457 }, 458 { 459 root: "git+ssh://", 460 ok: true, 461 }, 462 { 463 root: "http#://", 464 ok: false, 465 }, 466 { 467 root: "-config", 468 ok: false, 469 }, 470 { 471 root: "-config://", 472 ok: false, 473 }, 474 } 475 476 for _, test := range tests { 477 err := validateRepoRoot(test.root) 478 ok := err == nil 479 if ok != test.ok { 480 want := "error" 481 if test.ok { 482 want = "nil" 483 } 484 t.Errorf("validateRepoRoot(%q) = %q, want %s", test.root, err, want) 485 } 486 } 487 } 488 489 var govcsTests = []struct { 490 govcs string 491 path string 492 vcs string 493 ok bool 494 }{ 495 {"private:all", "is-public.com/foo", "zzz", false}, 496 {"private:all", "is-private.com/foo", "zzz", true}, 497 {"public:all", "is-public.com/foo", "zzz", true}, 498 {"public:all", "is-private.com/foo", "zzz", false}, 499 {"public:all,private:none", "is-public.com/foo", "zzz", true}, 500 {"public:all,private:none", "is-private.com/foo", "zzz", false}, 501 {"*:all", "is-public.com/foo", "zzz", true}, 502 {"golang.org:git", "golang.org/x/text", "zzz", false}, 503 {"golang.org:git", "golang.org/x/text", "git", true}, 504 {"golang.org:zzz", "golang.org/x/text", "zzz", true}, 505 {"golang.org:zzz", "golang.org/x/text", "git", false}, 506 {"golang.org:zzz", "golang.org/x/text", "zzz", true}, 507 {"golang.org:zzz", "golang.org/x/text", "git", false}, 508 {"golang.org:git|hg", "golang.org/x/text", "hg", true}, 509 {"golang.org:git|hg", "golang.org/x/text", "git", true}, 510 {"golang.org:git|hg", "golang.org/x/text", "zzz", false}, 511 {"golang.org:all", "golang.org/x/text", "hg", true}, 512 {"golang.org:all", "golang.org/x/text", "git", true}, 513 {"golang.org:all", "golang.org/x/text", "zzz", true}, 514 {"other.xyz/p:none,golang.org/x:git", "other.xyz/p/x", "git", false}, 515 {"other.xyz/p:none,golang.org/x:git", "unexpected.com", "git", false}, 516 {"other.xyz/p:none,golang.org/x:git", "golang.org/x/text", "zzz", false}, 517 {"other.xyz/p:none,golang.org/x:git", "golang.org/x/text", "git", true}, 518 {"other.xyz/p:none,golang.org/x:zzz", "golang.org/x/text", "zzz", true}, 519 {"other.xyz/p:none,golang.org/x:zzz", "golang.org/x/text", "git", false}, 520 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "hg", true}, 521 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "git", true}, 522 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/x/text", "zzz", false}, 523 {"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "hg", true}, 524 {"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "git", true}, 525 {"other.xyz/p:none,golang.org/x:all", "golang.org/x/text", "zzz", true}, 526 {"other.xyz/p:none,golang.org/x:git", "golang.org/y/text", "zzz", false}, 527 {"other.xyz/p:none,golang.org/x:git", "golang.org/y/text", "git", false}, 528 {"other.xyz/p:none,golang.org/x:zzz", "golang.org/y/text", "zzz", false}, 529 {"other.xyz/p:none,golang.org/x:zzz", "golang.org/y/text", "git", false}, 530 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "hg", false}, 531 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "git", false}, 532 {"other.xyz/p:none,golang.org/x:git|hg", "golang.org/y/text", "zzz", false}, 533 {"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "hg", false}, 534 {"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "git", false}, 535 {"other.xyz/p:none,golang.org/x:all", "golang.org/y/text", "zzz", false}, 536 } 537 538 func TestGOVCS(t *testing.T) { 539 for _, tt := range govcsTests { 540 cfg, err := parseGOVCS(tt.govcs) 541 if err != nil { 542 t.Errorf("parseGOVCS(%q): %v", tt.govcs, err) 543 continue 544 } 545 private := strings.HasPrefix(tt.path, "is-private") 546 ok := cfg.allow(tt.path, private, tt.vcs) 547 if ok != tt.ok { 548 t.Errorf("parseGOVCS(%q).allow(%q, %v, %q) = %v, want %v", 549 tt.govcs, tt.path, private, tt.vcs, ok, tt.ok) 550 } 551 } 552 } 553 554 var govcsErrors = []struct { 555 s string 556 err string 557 }{ 558 {`,`, `empty entry in GOVCS`}, 559 {`,x`, `empty entry in GOVCS`}, 560 {`x,`, `malformed entry in GOVCS (missing colon): "x"`}, 561 {`x:y,`, `empty entry in GOVCS`}, 562 {`x`, `malformed entry in GOVCS (missing colon): "x"`}, 563 {`x:`, `empty VCS list in GOVCS: "x:"`}, 564 {`x:|`, `empty VCS name in GOVCS: "x:|"`}, 565 {`x:y|`, `empty VCS name in GOVCS: "x:y|"`}, 566 {`x:|y`, `empty VCS name in GOVCS: "x:|y"`}, 567 {`x:y,z:`, `empty VCS list in GOVCS: "z:"`}, 568 {`x:y,z:|`, `empty VCS name in GOVCS: "z:|"`}, 569 {`x:y,z:|w`, `empty VCS name in GOVCS: "z:|w"`}, 570 {`x:y,z:w|`, `empty VCS name in GOVCS: "z:w|"`}, 571 {`x:y,z:w||v`, `empty VCS name in GOVCS: "z:w||v"`}, 572 {`x:y,x:z`, `unreachable pattern in GOVCS: "x:z" after "x:y"`}, 573 } 574 575 func TestGOVCSErrors(t *testing.T) { 576 for _, tt := range govcsErrors { 577 _, err := parseGOVCS(tt.s) 578 if err == nil || !strings.Contains(err.Error(), tt.err) { 579 t.Errorf("parseGOVCS(%s): err=%v, want %v", tt.s, err, tt.err) 580 } 581 } 582 }