blob: 77fe56ba0ff11191c898f23646d94bf8b265fab1 [file] [log] [blame]
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package fetch
import (
"context"
"errors"
"io"
"io/fs"
"os"
"path/filepath"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"golang.org/x/pkgsite/internal/derrors"
"golang.org/x/pkgsite/internal/proxy"
"golang.org/x/pkgsite/internal/testing/testhelper"
"golang.org/x/pkgsite/internal/version"
)
func TestDirectoryModuleGetterEmpty(t *testing.T) {
g, err := NewDirectoryModuleGetter("", "testdata/has_go_mod")
if err != nil {
t.Fatal(err)
}
if want := "example.com/testmod"; g.modulePath != want {
t.Errorf("got %q, want %q", g.modulePath, want)
}
_, err = NewDirectoryModuleGetter("", "testdata/no_go_mod")
if !errors.Is(err, derrors.BadModule) {
t.Errorf("got %v, want BadModule", err)
}
}
const multiModule = `
-- go.work --
go 1.21
use (
./foo
./bar
)
-- foo/go.mod --
module foo.com/foo
go 1.21
-- foo/foolog/f.go --
package foolog
const Log = 1
-- bar/go.mod --
module bar.com/bar
go 1.20
-- bar/barlog/b.go --
package barlog
const Log = 1
`
func TestGoPackagesModuleGetter(t *testing.T) {
modulePaths := map[string]string{ // dir -> module path
"foo": "foo.com/foo",
"bar": "bar.com/bar",
}
tests := []struct {
name string
dir string
}{
{"work dir", "."},
{"module dir", "foo"},
{"nested package dir", "foo/foolog"},
}
ctx := context.Background()
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
tempDir, files := testhelper.WriteTxtarToTempDir(t, multiModule)
dir := filepath.Join(tempDir, test.dir)
g, err := NewGoPackagesModuleGetter(ctx, dir, "all")
if err != nil {
t.Fatal(err)
}
for moduleDir, modulePath := range modulePaths {
t.Run("info", func(t *testing.T) {
got, err := g.Info(ctx, modulePath, "")
if err != nil {
t.Fatal(err)
}
if got, want := got.Version, LocalVersion; got != want {
t.Errorf("Info(%s): got version %s, want %s", modulePath, got, want)
}
})
mod := files[moduleDir+"/go.mod"]
t.Run("mod", func(t *testing.T) {
got, err := g.Mod(ctx, modulePath, "")
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(mod, string(got)); diff != "" {
t.Errorf("Mod(%q) mismatch [-want +got]:\n%s", modulePath, diff)
}
})
t.Run("contentdir", func(t *testing.T) {
fsys, err := g.ContentDir(ctx, modulePath, "")
if err != nil {
t.Fatal(err)
}
// Just check that the go.mod file is there and has the right contents.
got, err := fs.ReadFile(fsys, "go.mod")
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(mod, string(got)); diff != "" {
t.Errorf("fs.ReadFile(ContentDir(%q), %q) mismatch [-want +got]:\n%s", modulePath, "go.mod", diff)
}
})
t.Run("search", func(t *testing.T) {
tests := []struct {
query string
want []string
}{
{"log", []string{"barlog", "foolog"}},
{"barlog", []string{"barlog"}},
{"xxxxxx", nil},
}
for _, test := range tests {
results, err := g.Search(ctx, test.query, 10)
if err != nil {
t.Fatal(err)
}
var got []string
for _, r := range results {
got = append(got, r.Name)
}
if diff := cmp.Diff(test.want, got); diff != "" {
t.Errorf("Search(%s) mismatch [-want +got]:\n%s", test.query, diff)
}
}
})
}
})
}
}
func TestGoPackagesModuleGetter_Invalidation(t *testing.T) {
ctx := context.Background()
tempDir, _ := testhelper.WriteTxtarToTempDir(t, multiModule)
// Sleep before fetching the initial info, so that the written mtime will be
// considered reliable enough for caching by the getter.
time.Sleep(3 * time.Second)
g, err := NewGoPackagesModuleGetter(ctx, tempDir, "all")
if err != nil {
t.Fatal(err)
}
const fooPath = "foo.com/foo"
foo1, err := g.Info(ctx, fooPath, "")
if err != nil {
t.Fatal(err)
}
foo2, err := g.Info(ctx, fooPath, "")
if err != nil {
t.Fatal(err)
}
if !cmp.Equal(foo1, foo2) {
t.Errorf("Info(%q) returned inconsistent results: %v != %v", fooPath, foo1, foo2)
}
const barPath = "bar.com/bar"
bar1, err := g.Info(ctx, barPath, "")
if err != nil {
t.Fatal(err)
}
bar2, err := g.Info(ctx, barPath, "")
if err != nil {
t.Fatal(err)
}
if !cmp.Equal(bar1, bar2) {
t.Errorf("Info(%q) returned inconsistent results: %v != %v", barPath, bar1, bar2)
}
fpath := filepath.Join(tempDir, "foo", "foolog", "f.go")
newContent := []byte("package foolog; const Log = 3")
if err := os.WriteFile(fpath, newContent, 0600); err != nil {
t.Fatal(err)
}
foo3, err := g.Info(ctx, fooPath, "")
if err != nil {
t.Fatal(err)
}
if cmp.Equal(foo1, foo3) {
t.Errorf("Info(%q) results unexpectedly match: %v == %v", fooPath, foo1, foo3)
}
bar3, err := g.Info(ctx, barPath, "")
if err != nil {
t.Fatal(err)
}
if !cmp.Equal(bar1, bar2) {
t.Errorf("Info(%q) returned inconsistent results: %v != %v", barPath, bar1, bar3)
}
}
func TestEscapedPath(t *testing.T) {
for _, test := range []struct {
path, version, suffix string
want string
}{
{
"m.com", "v1", "info",
"dir/cache/download/m.com/@v/v1.info",
},
{
"github.com/aBc", "v2.3.4", "zip",
"dir/cache/download/github.com/a!bc/@v/v2.3.4.zip",
},
} {
g, err := NewModCacheGetter("dir")
if err != nil {
t.Fatal(err)
}
got, err := g.escapedPath(test.path, test.version, test.suffix)
if err != nil {
t.Fatal(err)
}
want, err := filepath.Abs(test.want)
if err != nil {
t.Fatal(err)
}
if got != want {
t.Errorf("%s, %s, %s: got %q, want %q", test.path, test.version, test.suffix, got, want)
}
}
}
func TestFSProxyGetter(t *testing.T) {
ctx := context.Background()
const (
modulePath = "github.com/jackc/pgio"
vers = "v1.0.0"
goMod = "module github.com/jackc/pgio\n\ngo 1.12\n"
)
ts, err := time.Parse(time.RFC3339, "2019-03-30T17:04:38Z")
if err != nil {
t.Fatal(err)
}
g, err := NewModCacheGetter("testdata/modcache")
if err != nil {
t.Fatal(err)
}
t.Run("info", func(t *testing.T) {
got, err := g.Info(ctx, modulePath, vers)
if err != nil {
t.Fatal(err)
}
want := &proxy.VersionInfo{Version: vers, Time: ts}
if !cmp.Equal(got, want) {
t.Errorf("got %+v, want %+v", got, want)
}
// Asking for latest should give the same version.
got, err = g.Info(ctx, modulePath, version.Latest)
if err != nil {
t.Fatal(err)
}
if !cmp.Equal(got, want) {
t.Errorf("got %+v, want %+v", got, want)
}
if _, err := g.Info(ctx, "nozip.com", vers); !errors.Is(err, derrors.NotFound) {
t.Errorf("got %v, want NotFound", err)
}
})
t.Run("mod", func(t *testing.T) {
got, err := g.Mod(ctx, modulePath, vers)
if err != nil {
t.Fatal(err)
}
want := []byte(goMod)
if !cmp.Equal(got, want) {
t.Errorf("got %q, want %q", got, want)
}
if _, err := g.Mod(ctx, "nozip.com", vers); !errors.Is(err, derrors.NotFound) {
t.Errorf("got %v, want NotFound", err)
}
})
t.Run("contentdir", func(t *testing.T) {
fsys, err := g.ContentDir(ctx, modulePath, vers)
if err != nil {
t.Fatal(err)
}
// Just check that the go.mod file is there and has the right contents.
f, err := fsys.Open("go.mod")
if err != nil {
t.Fatal(err)
}
defer f.Close()
got, err := io.ReadAll(f)
if err != nil {
t.Fatal(err)
}
want := []byte(goMod)
if !cmp.Equal(got, want) {
t.Errorf("got %q, want %q", got, want)
}
if _, err := g.ContentDir(ctx, "nozip.com", vers); !errors.Is(err, derrors.NotFound) {
t.Errorf("got %v, want NotFound", err)
}
})
}