github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/go/modfetch/coderepo_test.go (about) 1 // Copyright 2018 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 modfetch 6 7 import ( 8 "archive/zip" 9 "context" 10 "crypto/sha256" 11 "encoding/hex" 12 "flag" 13 "hash" 14 "io" 15 "log" 16 "os" 17 "path/filepath" 18 "reflect" 19 "strings" 20 "testing" 21 "time" 22 23 "github.com/go-asm/go/testenv" 24 25 "github.com/go-asm/go/cmd/go/cfg" 26 "github.com/go-asm/go/cmd/go/modfetch/codehost" 27 "github.com/go-asm/go/cmd/go/vcweb/vcstest" 28 29 "golang.org/x/mod/sumdb/dirhash" 30 ) 31 32 func TestMain(m *testing.M) { 33 flag.Parse() 34 if err := testMain(m); err != nil { 35 log.Fatal(err) 36 } 37 } 38 39 func testMain(m *testing.M) (err error) { 40 41 cfg.GOPROXY = "direct" 42 43 // The sum database is populated using a released version of the go command, 44 // but this test may include fixes for additional modules that previously 45 // could not be fetched. Since this test isn't executing any of the resolved 46 // code, bypass the sum database. 47 cfg.GOSUMDB = "off" 48 49 dir, err := os.MkdirTemp("", "gitrepo-test-") 50 if err != nil { 51 return err 52 } 53 defer func() { 54 if rmErr := os.RemoveAll(dir); err == nil { 55 err = rmErr 56 } 57 }() 58 59 cfg.GOMODCACHE = filepath.Join(dir, "modcache") 60 if err := os.Mkdir(cfg.GOMODCACHE, 0755); err != nil { 61 return err 62 } 63 64 srv, err := vcstest.NewServer() 65 if err != nil { 66 return err 67 } 68 defer func() { 69 if closeErr := srv.Close(); err == nil { 70 err = closeErr 71 } 72 }() 73 74 m.Run() 75 return nil 76 } 77 78 const ( 79 vgotest1git = "github.com/rsc/vgotest1" 80 vgotest1hg = "vcs-test.golang.org/hg/vgotest1.hg" 81 ) 82 83 var altVgotests = map[string]string{ 84 "hg": vgotest1hg, 85 } 86 87 type codeRepoTest struct { 88 vcs string 89 path string 90 mpath string 91 rev string 92 err string 93 version string 94 name string 95 short string 96 time time.Time 97 gomod string 98 gomodErr string 99 zip []string 100 zipErr string 101 zipSum string 102 zipFileHash string 103 } 104 105 var codeRepoTests = []codeRepoTest{ 106 { 107 vcs: "git", 108 path: "github.com/rsc/vgotest1", 109 rev: "v0.0.0", 110 version: "v0.0.0", 111 name: "80d85c5d4d17598a0e9055e7c175a32b415d6128", 112 short: "80d85c5d4d17", 113 time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC), 114 zip: []string{ 115 "LICENSE", 116 "README.md", 117 "pkg/p.go", 118 }, 119 zipSum: "h1:zVEjciLdlk/TPWCOyZo7k24T+tOKRQC+u8MKq/xS80I=", 120 zipFileHash: "738a00ddbfe8c329dce6b48e1f23c8e22a92db50f3cfb2653caa0d62676bc09c", 121 }, 122 { 123 vcs: "git", 124 path: "github.com/rsc/vgotest1", 125 rev: "v0.0.0-20180219231006-80d85c5d4d17", 126 version: "v0.0.0-20180219231006-80d85c5d4d17", 127 name: "80d85c5d4d17598a0e9055e7c175a32b415d6128", 128 short: "80d85c5d4d17", 129 time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC), 130 zip: []string{ 131 "LICENSE", 132 "README.md", 133 "pkg/p.go", 134 }, 135 zipSum: "h1:nOznk2xKsLGkTnXe0q9t1Ewt9jxK+oadtafSUqHM3Ec=", 136 zipFileHash: "bacb08f391e29d2eaaef8281b5c129ee6d890e608ee65877e0003c0181a766c8", 137 }, 138 { 139 vcs: "git", 140 path: "github.com/rsc/vgotest1", 141 rev: "v0.0.1-0.20180219231006-80d85c5d4d17", 142 err: `github.com/rsc/vgotest1@v0.0.1-0.20180219231006-80d85c5d4d17: invalid pseudo-version: tag (v0.0.0) found on revision 80d85c5d4d17 is already canonical, so should not be replaced with a pseudo-version derived from that tag`, 143 }, 144 { 145 vcs: "git", 146 path: "github.com/rsc/vgotest1", 147 rev: "v1.0.0", 148 version: "v1.0.0", 149 name: "80d85c5d4d17598a0e9055e7c175a32b415d6128", 150 short: "80d85c5d4d17", 151 time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC), 152 zip: []string{ 153 "LICENSE", 154 "README.md", 155 "pkg/p.go", 156 }, 157 zipSum: "h1:e040hOoWGeuJLawDjK9DW6med+cz9FxMFYDMOVG8ctQ=", 158 zipFileHash: "74caab65cfbea427c341fa815f3bb0378681d8f0e3cf62a7f207014263ec7be3", 159 }, 160 { 161 vcs: "git", 162 path: "github.com/rsc/vgotest1/v2", 163 rev: "v2.0.0", 164 version: "v2.0.0", 165 name: "45f53230a74ad275c7127e117ac46914c8126160", 166 short: "45f53230a74a", 167 time: time.Date(2018, 7, 19, 1, 21, 27, 0, time.UTC), 168 err: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0", 169 }, 170 { 171 vcs: "git", 172 path: "github.com/rsc/vgotest1", 173 rev: "80d85c5", 174 version: "v1.0.0", 175 name: "80d85c5d4d17598a0e9055e7c175a32b415d6128", 176 short: "80d85c5d4d17", 177 time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC), 178 zip: []string{ 179 "LICENSE", 180 "README.md", 181 "pkg/p.go", 182 }, 183 zipSum: "h1:e040hOoWGeuJLawDjK9DW6med+cz9FxMFYDMOVG8ctQ=", 184 zipFileHash: "74caab65cfbea427c341fa815f3bb0378681d8f0e3cf62a7f207014263ec7be3", 185 }, 186 { 187 vcs: "git", 188 path: "github.com/rsc/vgotest1", 189 rev: "mytag", 190 version: "v1.0.0", 191 name: "80d85c5d4d17598a0e9055e7c175a32b415d6128", 192 short: "80d85c5d4d17", 193 time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC), 194 zip: []string{ 195 "LICENSE", 196 "README.md", 197 "pkg/p.go", 198 }, 199 }, 200 { 201 vcs: "git", 202 path: "github.com/rsc/vgotest1/v2", 203 rev: "45f53230a", 204 version: "v2.0.0", 205 name: "45f53230a74ad275c7127e117ac46914c8126160", 206 short: "45f53230a74a", 207 time: time.Date(2018, 7, 19, 1, 21, 27, 0, time.UTC), 208 err: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0", 209 }, 210 { 211 vcs: "git", 212 path: "github.com/rsc/vgotest1/v54321", 213 rev: "80d85c5", 214 version: "v54321.0.0-20180219231006-80d85c5d4d17", 215 name: "80d85c5d4d17598a0e9055e7c175a32b415d6128", 216 short: "80d85c5d4d17", 217 time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC), 218 err: "missing github.com/rsc/vgotest1/go.mod and .../v54321/go.mod at revision 80d85c5d4d17", 219 }, 220 { 221 vcs: "git", 222 path: "github.com/rsc/vgotest1/submod", 223 rev: "v1.0.0", 224 err: "unknown revision submod/v1.0.0", 225 }, 226 { 227 vcs: "git", 228 path: "github.com/rsc/vgotest1/submod", 229 rev: "v1.0.3", 230 err: "unknown revision submod/v1.0.3", 231 }, 232 { 233 vcs: "git", 234 path: "github.com/rsc/vgotest1/submod", 235 rev: "v1.0.4", 236 version: "v1.0.4", 237 name: "8afe2b2efed96e0880ecd2a69b98a53b8c2738b6", 238 short: "8afe2b2efed9", 239 time: time.Date(2018, 2, 19, 23, 12, 7, 0, time.UTC), 240 gomod: "module \"github.com/vgotest1/submod\" // submod/go.mod\n", 241 zip: []string{ 242 "go.mod", 243 "pkg/p.go", 244 "LICENSE", 245 }, 246 zipSum: "h1:iMsJ/9uQsk6MnZNnJK311f11QiSlmN92Q2aSjCywuJY=", 247 zipFileHash: "95801bfa69c5197ae809af512946d22f22850068527cd78100ae3f176bc8043b", 248 }, 249 { 250 vcs: "git", 251 path: "github.com/rsc/vgotest1", 252 rev: "v1.1.0", 253 version: "v1.1.0", 254 name: "b769f2de407a4db81af9c5de0a06016d60d2ea09", 255 short: "b769f2de407a", 256 time: time.Date(2018, 2, 19, 23, 13, 36, 0, time.UTC), 257 gomod: "module \"github.com/rsc/vgotest1\" // root go.mod\nrequire \"github.com/rsc/vgotest1/submod\" v1.0.5\n", 258 zip: []string{ 259 "LICENSE", 260 "README.md", 261 "go.mod", 262 "pkg/p.go", 263 }, 264 zipSum: "h1:M69k7q+8bQ+QUpHov45Z/NoR8rj3DsQJUnXLWvf01+Q=", 265 zipFileHash: "58af45fb248d320ea471f568e006379e2b8d71d6d1663f9b19b2e00fd9ac9265", 266 }, 267 { 268 vcs: "git", 269 path: "github.com/rsc/vgotest1/v2", 270 rev: "v2.0.1", 271 version: "v2.0.1", 272 name: "ea65f87c8f52c15ea68f3bdd9925ef17e20d91e9", 273 short: "ea65f87c8f52", 274 time: time.Date(2018, 2, 19, 23, 14, 23, 0, time.UTC), 275 gomod: "module \"github.com/rsc/vgotest1/v2\" // root go.mod\n", 276 zipSum: "h1:QmgYy/zt+uoWhDpcsgrSVzYFvKtBEjl5zT/FRz9GTzA=", 277 zipFileHash: "1aedf1546d322a0121879ddfd6d0e8bfbd916d2cafbeb538ddb440e04b04b9ef", 278 }, 279 { 280 vcs: "git", 281 path: "github.com/rsc/vgotest1/v2", 282 rev: "v2.0.3", 283 version: "v2.0.3", 284 name: "f18795870fb14388a21ef3ebc1d75911c8694f31", 285 short: "f18795870fb1", 286 time: time.Date(2018, 2, 19, 23, 16, 4, 0, time.UTC), 287 err: "github.com/rsc/vgotest1/v2/go.mod has non-.../v2 module path \"github.com/rsc/vgotest\" at revision v2.0.3", 288 }, 289 { 290 vcs: "git", 291 path: "github.com/rsc/vgotest1/v2", 292 rev: "v2.0.4", 293 version: "v2.0.4", 294 name: "1f863feb76bc7029b78b21c5375644838962f88d", 295 short: "1f863feb76bc", 296 time: time.Date(2018, 2, 20, 0, 3, 38, 0, time.UTC), 297 err: "github.com/rsc/vgotest1/go.mod and .../v2/go.mod both have .../v2 module paths at revision v2.0.4", 298 }, 299 { 300 vcs: "git", 301 path: "github.com/rsc/vgotest1/v2", 302 rev: "v2.0.5", 303 version: "v2.0.5", 304 name: "2f615117ce481c8efef46e0cc0b4b4dccfac8fea", 305 short: "2f615117ce48", 306 time: time.Date(2018, 2, 20, 0, 3, 59, 0, time.UTC), 307 gomod: "module \"github.com/rsc/vgotest1/v2\" // v2/go.mod\n", 308 zipSum: "h1:RIEb9q1SUSEQOzMn0zfl/LQxGFWlhWEAdeEguf1MLGU=", 309 zipFileHash: "7d92c2c328c5e9b0694101353705d5843746ec1d93a1e986d0da54c8a14dfe6d", 310 }, 311 { 312 // redirect to github 313 vcs: "git", 314 path: "rsc.io/quote", 315 rev: "v1.0.0", 316 version: "v1.0.0", 317 name: "f488df80bcdbd3e5bafdc24ad7d1e79e83edd7e6", 318 short: "f488df80bcdb", 319 time: time.Date(2018, 2, 14, 0, 45, 20, 0, time.UTC), 320 gomod: "module \"rsc.io/quote\"\n", 321 zipSum: "h1:haUSojyo3j2M9g7CEUFG8Na09dtn7QKxvPGaPVQdGwM=", 322 zipFileHash: "5c08ba2c09a364f93704aaa780e7504346102c6ef4fe1333a11f09904a732078", 323 }, 324 { 325 // redirect to static hosting proxy 326 vcs: "mod", 327 path: "swtch.com/testmod", 328 rev: "v1.0.0", 329 version: "v1.0.0", 330 // NO name or short - we intentionally ignore those in the proxy protocol 331 time: time.Date(1972, 7, 18, 12, 34, 56, 0, time.UTC), 332 gomod: "module \"swtch.com/testmod\"\n", 333 }, 334 { 335 // redirect to googlesource 336 vcs: "git", 337 path: "golang.org/x/text", 338 rev: "4e4a3210bb", 339 version: "v0.3.1-0.20180208041248-4e4a3210bb54", 340 name: "4e4a3210bb54bb31f6ab2cdca2edcc0b50c420c1", 341 short: "4e4a3210bb54", 342 time: time.Date(2018, 2, 8, 4, 12, 48, 0, time.UTC), 343 zipSum: "h1:Yxu6pHX9X2RECiuw/Q5/4uvajuaowck8zOFKXgbfNBk=", 344 zipFileHash: "ac2c165a5c10aa5a7545dea60a08e019270b982fa6c8bdcb5943931de64922fe", 345 }, 346 { 347 vcs: "git", 348 path: "github.com/pkg/errors", 349 rev: "v0.8.0", 350 version: "v0.8.0", 351 name: "645ef00459ed84a119197bfb8d8205042c6df63d", 352 short: "645ef00459ed", 353 time: time.Date(2016, 9, 29, 1, 48, 1, 0, time.UTC), 354 zipSum: "h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=", 355 zipFileHash: "e4fa69ba057356614edbc1da881a7d3ebb688505be49f65965686bcb859e2fae", 356 }, 357 { 358 // package in subdirectory - custom domain 359 // In general we can't reject these definitively in Lookup, 360 // but gopkg.in is special. 361 vcs: "git", 362 path: "gopkg.in/yaml.v2/abc", 363 err: "invalid module path \"gopkg.in/yaml.v2/abc\"", 364 }, 365 { 366 // package in subdirectory - github 367 // Because it's a package, Stat should fail entirely. 368 vcs: "git", 369 path: "github.com/rsc/quote/buggy", 370 rev: "c4d4236f", 371 err: "missing github.com/rsc/quote/buggy/go.mod at revision c4d4236f9242", 372 }, 373 { 374 vcs: "git", 375 path: "gopkg.in/yaml.v2", 376 rev: "d670f940", 377 version: "v2.0.0", 378 name: "d670f9405373e636a5a2765eea47fac0c9bc91a4", 379 short: "d670f9405373", 380 time: time.Date(2018, 1, 9, 11, 43, 31, 0, time.UTC), 381 gomod: "module gopkg.in/yaml.v2\n", 382 zipSum: "h1:uUkhRGrsEyx/laRdeS6YIQKIys8pg+lRSRdVMTYjivs=", 383 zipFileHash: "7b0a141b1b0b49772ab4eecfd11dfd6609a94a5e868cab04a3abb1861ffaa877", 384 }, 385 { 386 vcs: "git", 387 path: "gopkg.in/check.v1", 388 rev: "20d25e280405", 389 version: "v1.0.0-20161208181325-20d25e280405", 390 name: "20d25e2804050c1cd24a7eea1e7a6447dd0e74ec", 391 short: "20d25e280405", 392 time: time.Date(2016, 12, 8, 18, 13, 25, 0, time.UTC), 393 gomod: "module gopkg.in/check.v1\n", 394 zipSum: "h1:829vOVxxusYHC+IqBtkX5mbKtsY9fheQiQn0MZRVLfQ=", 395 zipFileHash: "9e7cb3f4f1e66d722306442b0dbe1f6f43d74d1736d54c510537bdfb1d6f432f", 396 }, 397 { 398 vcs: "git", 399 path: "vcs-test.golang.org/go/mod/gitrepo1", 400 rev: "master", 401 version: "v1.2.4-annotated", 402 name: "ede458df7cd0fdca520df19a33158086a8a68e81", 403 short: "ede458df7cd0", 404 time: time.Date(2018, 4, 17, 19, 43, 22, 0, time.UTC), 405 gomod: "module vcs-test.golang.org/go/mod/gitrepo1\n", 406 zipSum: "h1:YJYZRsM9BHFTlVr8YADjT0cJH8uFIDtoc5NLiVqZEx8=", 407 zipFileHash: "c15e49d58b7a4c37966cbe5bc01a0330cd5f2927e990e1839bda1d407766d9c5", 408 }, 409 { 410 vcs: "git", 411 path: "gopkg.in/natefinch/lumberjack.v2", 412 // This repo has a v2.1 tag. 413 // We only allow semver references to tags that are fully qualified, as in v2.1.0. 414 // Because we can't record v2.1.0 (the actual tag is v2.1), we record a pseudo-version 415 // instead, same as if the tag were any other non-version-looking string. 416 // We use a v2 pseudo-version here because of the .v2 in the path, not because 417 // of the v2 in the rev. 418 rev: "v2.1", // non-canonical semantic version turns into pseudo-version 419 version: "v2.0.0-20170531160350-a96e63847dc3", 420 name: "a96e63847dc3c67d17befa69c303767e2f84e54f", 421 short: "a96e63847dc3", 422 time: time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC), 423 gomod: "module gopkg.in/natefinch/lumberjack.v2\n", 424 }, 425 { 426 vcs: "git", 427 path: "vcs-test.golang.org/go/v2module/v2", 428 rev: "v2.0.0", 429 version: "v2.0.0", 430 name: "203b91c896acd173aa719e4cdcb7d463c4b090fa", 431 short: "203b91c896ac", 432 time: time.Date(2019, 4, 3, 15, 52, 15, 0, time.UTC), 433 gomod: "module vcs-test.golang.org/go/v2module/v2\n\ngo 1.12\n", 434 zipSum: "h1:JItBZ+gwA5WvtZEGEbuDL4lUttGtLrs53lmdurq3bOg=", 435 zipFileHash: "9ea9ae1673cffcc44b7fdd3cc89953d68c102449b46c982dbf085e4f2e394da5", 436 }, 437 { 438 // Git branch with a semver name, +incompatible version, and no go.mod file. 439 vcs: "git", 440 path: "vcs-test.golang.org/go/mod/gitrepo1", 441 rev: "v2.3.4+incompatible", 442 err: `resolves to version v2.0.1+incompatible (v2.3.4 is not a tag)`, 443 }, 444 { 445 // Git branch with a semver name, matching go.mod file, and compatible version. 446 vcs: "git", 447 path: "vcs-test.golang.org/git/semver-branch.git", 448 rev: "v1.0.0", 449 err: `resolves to version v0.1.1-0.20220202191944-09c4d8f6938c (v1.0.0 is not a tag)`, 450 }, 451 { 452 // Git branch with a semver name, matching go.mod file, and disallowed +incompatible version. 453 // The version/tag mismatch takes precedence over the +incompatible mismatched. 454 vcs: "git", 455 path: "vcs-test.golang.org/git/semver-branch.git", 456 rev: "v2.0.0+incompatible", 457 err: `resolves to version v0.1.0 (v2.0.0 is not a tag)`, 458 }, 459 { 460 // Git branch with a semver name, matching go.mod file, and mismatched version. 461 // The version/tag mismatch takes precedence over the +incompatible mismatched. 462 vcs: "git", 463 path: "vcs-test.golang.org/git/semver-branch.git", 464 rev: "v2.0.0", 465 err: `resolves to version v0.1.0 (v2.0.0 is not a tag)`, 466 }, 467 { 468 // v3.0.0-devel is the same as tag v4.0.0-beta.1, but v4.0.0-beta.1 would 469 // not be allowed because it is incompatible and a go.mod file exists. 470 // The error message should refer to a valid pseudo-version, not the 471 // unusable semver tag. 472 vcs: "git", 473 path: "vcs-test.golang.org/git/semver-branch.git", 474 rev: "v3.0.0-devel", 475 err: `resolves to version v0.1.1-0.20220203155313-d59622f6e4d7 (v3.0.0-devel is not a tag)`, 476 }, 477 478 // If v2/go.mod exists, then we should prefer to match the "v2" 479 // pseudo-versions to the nested module, and resolve the module in the parent 480 // directory to only compatible versions. 481 // 482 // However (https://go.dev/issue/51324), previous versions of the 'go' command 483 // didn't always do so, so if the user explicitly requests a +incompatible 484 // version (as would be present in an existing go.mod file), we should 485 // continue to allow it. 486 { 487 vcs: "git", 488 path: "vcs-test.golang.org/git/v2sub.git", 489 rev: "80beb17a1603", 490 version: "v0.0.0-20220222205507-80beb17a1603", 491 name: "80beb17a16036f17a5aedd1bb5bd6d407b3c6dc5", 492 short: "80beb17a1603", 493 time: time.Date(2022, 2, 22, 20, 55, 7, 0, time.UTC), 494 }, 495 { 496 vcs: "git", 497 path: "vcs-test.golang.org/git/v2sub.git", 498 rev: "v2.0.0", 499 err: `module contains a go.mod file, so module path must match major version ("vcs-test.golang.org/git/v2sub.git/v2")`, 500 }, 501 { 502 vcs: "git", 503 path: "vcs-test.golang.org/git/v2sub.git", 504 rev: "v2.0.1-0.20220222205507-80beb17a1603", 505 err: `module contains a go.mod file, so module path must match major version ("vcs-test.golang.org/git/v2sub.git/v2")`, 506 }, 507 { 508 vcs: "git", 509 path: "vcs-test.golang.org/git/v2sub.git", 510 rev: "v2.0.0+incompatible", 511 version: "v2.0.0+incompatible", 512 name: "5fcd3eaeeb391d399f562fd45a50dac9fc34ae8b", 513 short: "5fcd3eaeeb39", 514 time: time.Date(2022, 2, 22, 20, 53, 33, 0, time.UTC), 515 }, 516 { 517 vcs: "git", 518 path: "vcs-test.golang.org/git/v2sub.git", 519 rev: "v2.0.1-0.20220222205507-80beb17a1603+incompatible", 520 version: "v2.0.1-0.20220222205507-80beb17a1603+incompatible", 521 name: "80beb17a16036f17a5aedd1bb5bd6d407b3c6dc5", 522 short: "80beb17a1603", 523 time: time.Date(2022, 2, 22, 20, 55, 7, 0, time.UTC), 524 }, 525 526 // A version tag with explicit build metadata is valid but not canonical. 527 // It should resolve to a pseudo-version based on the same tag. 528 { 529 vcs: "git", 530 path: "vcs-test.golang.org/git/odd-tags.git", 531 rev: "v0.1.0+build-metadata", 532 version: "v0.1.1-0.20220223184835-9d863d525bbf", 533 name: "9d863d525bbfcc8eda09364738c4032393711a56", 534 short: "9d863d525bbf", 535 time: time.Date(2022, 2, 23, 18, 48, 35, 0, time.UTC), 536 }, 537 { 538 vcs: "git", 539 path: "vcs-test.golang.org/git/odd-tags.git", 540 rev: "9d863d525bbf", 541 version: "v0.1.1-0.20220223184835-9d863d525bbf", 542 name: "9d863d525bbfcc8eda09364738c4032393711a56", 543 short: "9d863d525bbf", 544 time: time.Date(2022, 2, 23, 18, 48, 35, 0, time.UTC), 545 }, 546 { 547 vcs: "git", 548 path: "vcs-test.golang.org/git/odd-tags.git", 549 rev: "latest", 550 version: "v0.1.1-0.20220223184835-9d863d525bbf", 551 name: "9d863d525bbfcc8eda09364738c4032393711a56", 552 short: "9d863d525bbf", 553 time: time.Date(2022, 2, 23, 18, 48, 35, 0, time.UTC), 554 }, 555 556 // A version tag with an erroneous "+incompatible" suffix should resolve using 557 // only the prefix before the "+incompatible" suffix, not the "+incompatible" 558 // tag itself. (Otherwise, we would potentially have two different commits 559 // both named "v2.0.0+incompatible".) However, the tag is still valid semver 560 // and can still be used as the base for an unambiguous pseudo-version. 561 { 562 vcs: "git", 563 path: "vcs-test.golang.org/git/odd-tags.git", 564 rev: "v2.0.0+incompatible", 565 err: `unknown revision v2.0.0`, 566 }, 567 { 568 vcs: "git", 569 path: "vcs-test.golang.org/git/odd-tags.git", 570 rev: "12d19af20458", 571 version: "v2.0.1-0.20220223184802-12d19af20458+incompatible", 572 name: "12d19af204585b0db3d2a876ceddf5b9323f5a4a", 573 short: "12d19af20458", 574 time: time.Date(2022, 2, 23, 18, 48, 2, 0, time.UTC), 575 }, 576 577 // Similarly, a pseudo-version must resolve to the named commit, even if a tag 578 // matching that pseudo-version is present on a *different* commit. 579 { 580 vcs: "git", 581 path: "vcs-test.golang.org/git/odd-tags.git", 582 rev: "v3.0.0-20220223184802-12d19af20458", 583 version: "v3.0.0-20220223184802-12d19af20458+incompatible", 584 name: "12d19af204585b0db3d2a876ceddf5b9323f5a4a", 585 short: "12d19af20458", 586 time: time.Date(2022, 2, 23, 18, 48, 2, 0, time.UTC), 587 }, 588 } 589 590 func TestCodeRepo(t *testing.T) { 591 testenv.MustHaveExternalNetwork(t) 592 tmpdir := t.TempDir() 593 594 for _, tt := range codeRepoTests { 595 f := func(tt codeRepoTest) func(t *testing.T) { 596 return func(t *testing.T) { 597 if strings.Contains(tt.path, "gopkg.in") { 598 testenv.SkipFlaky(t, 54503) 599 } 600 601 t.Parallel() 602 if tt.vcs != "mod" { 603 testenv.MustHaveExecPath(t, tt.vcs) 604 } 605 ctx := context.Background() 606 607 repo := Lookup(ctx, "direct", tt.path) 608 609 if tt.mpath == "" { 610 tt.mpath = tt.path 611 } 612 if mpath := repo.ModulePath(); mpath != tt.mpath { 613 t.Errorf("repo.ModulePath() = %q, want %q", mpath, tt.mpath) 614 } 615 616 info, err := repo.Stat(ctx, tt.rev) 617 if err != nil { 618 if tt.err != "" { 619 if !strings.Contains(err.Error(), tt.err) { 620 t.Fatalf("repoStat(%q): %v, wanted %q", tt.rev, err, tt.err) 621 } 622 return 623 } 624 t.Fatalf("repo.Stat(%q): %v", tt.rev, err) 625 } 626 if tt.err != "" { 627 t.Errorf("repo.Stat(%q): success, wanted error", tt.rev) 628 } 629 if info.Version != tt.version { 630 t.Errorf("info.Version = %q, want %q", info.Version, tt.version) 631 } 632 if info.Name != tt.name { 633 t.Errorf("info.Name = %q, want %q", info.Name, tt.name) 634 } 635 if info.Short != tt.short { 636 t.Errorf("info.Short = %q, want %q", info.Short, tt.short) 637 } 638 if !info.Time.Equal(tt.time) { 639 t.Errorf("info.Time = %v, want %v", info.Time, tt.time) 640 } 641 642 if tt.gomod != "" || tt.gomodErr != "" { 643 data, err := repo.GoMod(ctx, tt.version) 644 if err != nil && tt.gomodErr == "" { 645 t.Errorf("repo.GoMod(%q): %v", tt.version, err) 646 } else if err != nil && tt.gomodErr != "" { 647 if err.Error() != tt.gomodErr { 648 t.Errorf("repo.GoMod(%q): %v, want %q", tt.version, err, tt.gomodErr) 649 } 650 } else if tt.gomodErr != "" { 651 t.Errorf("repo.GoMod(%q) = %q, want error %q", tt.version, data, tt.gomodErr) 652 } else if string(data) != tt.gomod { 653 t.Errorf("repo.GoMod(%q) = %q, want %q", tt.version, data, tt.gomod) 654 } 655 } 656 657 needHash := !testing.Short() && (tt.zipFileHash != "" || tt.zipSum != "") 658 if tt.zip != nil || tt.zipErr != "" || needHash { 659 f, err := os.CreateTemp(tmpdir, tt.version+".zip.") 660 if err != nil { 661 t.Fatalf("os.CreateTemp: %v", err) 662 } 663 zipfile := f.Name() 664 defer func() { 665 f.Close() 666 os.Remove(zipfile) 667 }() 668 669 var w io.Writer 670 var h hash.Hash 671 if needHash { 672 h = sha256.New() 673 w = io.MultiWriter(f, h) 674 } else { 675 w = f 676 } 677 err = repo.Zip(ctx, w, tt.version) 678 f.Close() 679 if err != nil { 680 if tt.zipErr != "" { 681 if err.Error() == tt.zipErr { 682 return 683 } 684 t.Fatalf("repo.Zip(%q): %v, want error %q", tt.version, err, tt.zipErr) 685 } 686 t.Fatalf("repo.Zip(%q): %v", tt.version, err) 687 } 688 if tt.zipErr != "" { 689 t.Errorf("repo.Zip(%q): success, want error %q", tt.version, tt.zipErr) 690 } 691 692 if tt.zip != nil { 693 prefix := tt.path + "@" + tt.version + "/" 694 z, err := zip.OpenReader(zipfile) 695 if err != nil { 696 t.Fatalf("open zip %s: %v", zipfile, err) 697 } 698 var names []string 699 for _, file := range z.File { 700 if !strings.HasPrefix(file.Name, prefix) { 701 t.Errorf("zip entry %v does not start with prefix %v", file.Name, prefix) 702 continue 703 } 704 names = append(names, file.Name[len(prefix):]) 705 } 706 z.Close() 707 if !reflect.DeepEqual(names, tt.zip) { 708 t.Fatalf("zip = %v\nwant %v\n", names, tt.zip) 709 } 710 } 711 712 if needHash { 713 sum, err := dirhash.HashZip(zipfile, dirhash.Hash1) 714 if err != nil { 715 t.Errorf("repo.Zip(%q): %v", tt.version, err) 716 } else if sum != tt.zipSum { 717 t.Errorf("repo.Zip(%q): got file with sum %q, want %q", tt.version, sum, tt.zipSum) 718 } else if zipFileHash := hex.EncodeToString(h.Sum(nil)); zipFileHash != tt.zipFileHash { 719 t.Errorf("repo.Zip(%q): got file with hash %q, want %q (but content has correct sum)", tt.version, zipFileHash, tt.zipFileHash) 720 } 721 } 722 } 723 } 724 } 725 t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f(tt)) 726 if strings.HasPrefix(tt.path, vgotest1git) { 727 for vcs, alt := range altVgotests { 728 altTest := tt 729 altTest.vcs = vcs 730 altTest.path = alt + strings.TrimPrefix(altTest.path, vgotest1git) 731 if strings.HasPrefix(altTest.mpath, vgotest1git) { 732 altTest.mpath = alt + strings.TrimPrefix(altTest.mpath, vgotest1git) 733 } 734 var m map[string]string 735 if alt == vgotest1hg { 736 m = hgmap 737 } 738 altTest.version = remap(altTest.version, m) 739 altTest.name = remap(altTest.name, m) 740 altTest.short = remap(altTest.short, m) 741 altTest.rev = remap(altTest.rev, m) 742 altTest.err = remap(altTest.err, m) 743 altTest.gomodErr = remap(altTest.gomodErr, m) 744 altTest.zipErr = remap(altTest.zipErr, m) 745 altTest.zipSum = "" 746 altTest.zipFileHash = "" 747 t.Run(strings.ReplaceAll(altTest.path, "/", "_")+"/"+altTest.rev, f(altTest)) 748 } 749 } 750 } 751 } 752 753 var hgmap = map[string]string{ 754 "github.com/rsc/vgotest1": "vcs-test.golang.org/hg/vgotest1.hg", 755 "f18795870fb14388a21ef3ebc1d75911c8694f31": "a9ad6d1d14eb544f459f446210c7eb3b009807c6", 756 "ea65f87c8f52c15ea68f3bdd9925ef17e20d91e9": "f1fc0f22021b638d073d31c752847e7bf385def7", 757 "b769f2de407a4db81af9c5de0a06016d60d2ea09": "92c7eb888b4fac17f1c6bd2e1060a1b881a3b832", 758 "8afe2b2efed96e0880ecd2a69b98a53b8c2738b6": "4e58084d459ae7e79c8c2264d0e8e9a92eb5cd44", 759 "2f615117ce481c8efef46e0cc0b4b4dccfac8fea": "879ea98f7743c8eff54f59a918f3a24123d1cf46", 760 "80d85c5d4d17598a0e9055e7c175a32b415d6128": "e125018e286a4b09061079a81e7b537070b7ff71", 761 "1f863feb76bc7029b78b21c5375644838962f88d": "bf63880162304a9337477f3858f5b7e255c75459", 762 "45f53230a74ad275c7127e117ac46914c8126160": "814fce58e83abd5bf2a13892e0b0e1198abefcd4", 763 } 764 765 func remap(name string, m map[string]string) string { 766 if m[name] != "" { 767 return m[name] 768 } 769 if codehost.AllHex(name) { 770 for k, v := range m { 771 if strings.HasPrefix(k, name) { 772 return v[:len(name)] 773 } 774 } 775 } 776 for k, v := range m { 777 name = strings.ReplaceAll(name, k, v) 778 if codehost.AllHex(k) { 779 name = strings.ReplaceAll(name, k[:12], v[:12]) 780 } 781 } 782 return name 783 } 784 785 var codeRepoVersionsTests = []struct { 786 vcs string 787 path string 788 prefix string 789 versions []string 790 }{ 791 { 792 vcs: "git", 793 path: "github.com/rsc/vgotest1", 794 versions: []string{"v0.0.0", "v0.0.1", "v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3", "v1.1.0"}, 795 }, 796 { 797 vcs: "git", 798 path: "github.com/rsc/vgotest1", 799 prefix: "v1.0", 800 versions: []string{"v1.0.0", "v1.0.1", "v1.0.2", "v1.0.3"}, 801 }, 802 { 803 vcs: "git", 804 path: "github.com/rsc/vgotest1/v2", 805 versions: []string{"v2.0.0", "v2.0.1", "v2.0.2", "v2.0.3", "v2.0.4", "v2.0.5", "v2.0.6"}, 806 }, 807 { 808 vcs: "mod", 809 path: "swtch.com/testmod", 810 versions: []string{"v1.0.0", "v1.1.1"}, 811 }, 812 { 813 vcs: "git", 814 path: "vcs-test.golang.org/git/odd-tags.git", 815 versions: nil, 816 }, 817 } 818 819 func TestCodeRepoVersions(t *testing.T) { 820 testenv.MustHaveExternalNetwork(t) 821 822 for _, tt := range codeRepoVersionsTests { 823 tt := tt 824 t.Run(strings.ReplaceAll(tt.path, "/", "_"), func(t *testing.T) { 825 if strings.Contains(tt.path, "gopkg.in") { 826 testenv.SkipFlaky(t, 54503) 827 } 828 829 t.Parallel() 830 if tt.vcs != "mod" { 831 testenv.MustHaveExecPath(t, tt.vcs) 832 } 833 ctx := context.Background() 834 835 repo := Lookup(ctx, "direct", tt.path) 836 list, err := repo.Versions(ctx, tt.prefix) 837 if err != nil { 838 t.Fatalf("Versions(%q): %v", tt.prefix, err) 839 } 840 if !reflect.DeepEqual(list.List, tt.versions) { 841 t.Fatalf("Versions(%q):\nhave %v\nwant %v", tt.prefix, list, tt.versions) 842 } 843 }) 844 } 845 } 846 847 var latestTests = []struct { 848 vcs string 849 path string 850 version string 851 err string 852 }{ 853 { 854 vcs: "git", 855 path: "github.com/rsc/empty", 856 err: "no commits", 857 }, 858 { 859 vcs: "git", 860 path: "github.com/rsc/vgotest1", 861 err: `github.com/rsc/vgotest1@v0.0.0-20180219223237-a08abb797a67: invalid version: go.mod has post-v0 module path "github.com/vgotest1/v2" at revision a08abb797a67`, 862 }, 863 { 864 vcs: "git", 865 path: "github.com/rsc/vgotest1/v2", 866 err: `github.com/rsc/vgotest1/v2@v2.0.0-20180219223237-a08abb797a67: invalid version: github.com/rsc/vgotest1/go.mod and .../v2/go.mod both have .../v2 module paths at revision a08abb797a67`, 867 }, 868 { 869 vcs: "git", 870 path: "github.com/rsc/vgotest1/subdir", 871 err: "github.com/rsc/vgotest1/subdir@v0.0.0-20180219223237-a08abb797a67: invalid version: missing github.com/rsc/vgotest1/subdir/go.mod at revision a08abb797a67", 872 }, 873 { 874 vcs: "git", 875 path: "vcs-test.golang.org/git/commit-after-tag.git", 876 version: "v1.0.1-0.20190715211727-b325d8217783", 877 }, 878 { 879 vcs: "git", 880 path: "vcs-test.golang.org/git/no-tags.git", 881 version: "v0.0.0-20190715212047-e706ba1d9f6d", 882 }, 883 { 884 vcs: "mod", 885 path: "swtch.com/testmod", 886 version: "v1.1.1", 887 }, 888 } 889 890 func TestLatest(t *testing.T) { 891 testenv.MustHaveExternalNetwork(t) 892 893 for _, tt := range latestTests { 894 name := strings.ReplaceAll(tt.path, "/", "_") 895 t.Run(name, func(t *testing.T) { 896 tt := tt 897 t.Parallel() 898 if tt.vcs != "mod" { 899 testenv.MustHaveExecPath(t, tt.vcs) 900 } 901 ctx := context.Background() 902 903 repo := Lookup(ctx, "direct", tt.path) 904 info, err := repo.Latest(ctx) 905 if err != nil { 906 if tt.err != "" { 907 if err.Error() == tt.err { 908 return 909 } 910 t.Fatalf("Latest(): %v, want %q", err, tt.err) 911 } 912 t.Fatalf("Latest(): %v", err) 913 } 914 if tt.err != "" { 915 t.Fatalf("Latest() = %v, want error %q", info.Version, tt.err) 916 } 917 if info.Version != tt.version { 918 t.Fatalf("Latest() = %v, want %v", info.Version, tt.version) 919 } 920 }) 921 } 922 } 923 924 // fixedTagsRepo is a fake codehost.Repo that returns a fixed list of tags 925 type fixedTagsRepo struct { 926 tags []string 927 codehost.Repo 928 } 929 930 func (ch *fixedTagsRepo) Tags(ctx context.Context, prefix string) (*codehost.Tags, error) { 931 tags := &codehost.Tags{} 932 for _, t := range ch.tags { 933 tags.List = append(tags.List, codehost.Tag{Name: t}) 934 } 935 return tags, nil 936 } 937 938 func TestNonCanonicalSemver(t *testing.T) { 939 t.Parallel() 940 ctx := context.Background() 941 942 root := "golang.org/x/issue24476" 943 ch := &fixedTagsRepo{ 944 tags: []string{ 945 "", "huh?", "1.0.1", 946 // what about "version 1 dot dogcow"? 947 "v1.🐕.🐄", 948 "v1", "v0.1", 949 // and one normal one that should pass through 950 "v1.0.1", 951 }, 952 } 953 954 cr, err := newCodeRepo(ch, root, root) 955 if err != nil { 956 t.Fatal(err) 957 } 958 959 v, err := cr.Versions(ctx, "") 960 if err != nil { 961 t.Fatal(err) 962 } 963 if len(v.List) != 1 || v.List[0] != "v1.0.1" { 964 t.Fatal("unexpected versions returned:", v) 965 } 966 }