Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/go/internal/modfetch/coderepo_test.go

Documentation: cmd/go/internal/modfetch

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

View as plain text