blob: 4768088a492f553b76a2fa06d74fc3ed785c0b56 [file] [log] [blame]
// Copyright 2019 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"
"io/fs"
"net/http"
"os"
"sort"
"testing"
"time"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/licenses"
"golang.org/x/pkgsite/internal/log"
"golang.org/x/pkgsite/internal/proxy"
"golang.org/x/pkgsite/internal/proxy/proxytest"
"golang.org/x/pkgsite/internal/source"
"golang.org/x/pkgsite/internal/stdlib"
"golang.org/x/pkgsite/internal/testing/sample"
"golang.org/x/pkgsite/internal/testing/testhelper"
)
var testProxyCommitTime = time.Date(2019, 1, 30, 0, 0, 0, 0, time.UTC)
// cleanFetchResult adds missing information to a given FetchResult and returns
// it. It's meant to be used with test cases in fetchdata_test and should be called
// only once for each test case. The missing information is added here to avoid
// having to hardcode it into each test case.
func cleanFetchResult(t *testing.T, fr *FetchResult) *FetchResult {
t.Helper()
fr.ModulePath = fr.Module.ModulePath
if fr.GoModPath == "" {
fr.GoModPath = fr.ModulePath
}
if fr.Status == 0 {
fr.Status = 200
}
if fr.Module.Version == "" {
fr.Module.Version = sample.VersionString
}
if fr.RequestedVersion == "" {
fr.RequestedVersion = fr.Module.Version
}
fr.ResolvedVersion = fr.Module.Version
if fr.Module.CommitTime.IsZero() {
fr.Module.CommitTime = testProxyCommitTime
}
shouldSetPVS := (fr.PackageVersionStates == nil)
for _, u := range fr.Module.Units {
u.UnitMeta = internal.UnitMeta{
ModuleInfo: internal.ModuleInfo{
ModulePath: fr.Module.ModulePath,
Version: fr.Module.Version,
IsRedistributable: fr.Module.IsRedistributable,
},
Path: u.Path,
Name: u.Name,
IsRedistributable: u.IsRedistributable,
Licenses: u.Licenses,
}
if u.IsPackage() && shouldSetPVS {
fr.PackageVersionStates = append(
fr.PackageVersionStates, &internal.PackageVersionState{
PackagePath: u.Path,
ModulePath: fr.Module.ModulePath,
Version: fr.Module.Version,
Status: http.StatusOK,
},
)
}
for _, d := range u.Documentation {
for _, s := range d.API {
s.GOOS = d.GOOS
s.GOARCH = d.GOARCH
}
}
}
return fr
}
func cleanLicenses(t *testing.T, fr *FetchResult, detector *licenses.Detector) *FetchResult {
fr.Module.Licenses = nil
for _, u := range fr.Module.Units {
u.Licenses = nil
}
allLicenses := detector.AllLicenses()
if len(allLicenses) > 0 {
fr.Module.Licenses = allLicenses
fr.Module.IsRedistributable = true
for _, d := range fr.Module.Units {
isRedist, lics := detector.PackageInfo(internal.Suffix(d.Path, fr.ModulePath))
for _, l := range lics {
d.Licenses = append(d.Licenses, l.Metadata)
}
d.IsRedistributable = isRedist
}
}
return fr
}
// updateFetchResultVersions updates units' and package version states' version
// based on the type of fetching. Should be used for test cases in fetchdata_test.
func updateFetchResultVersions(t *testing.T, fr *FetchResult, local bool) *FetchResult {
t.Helper()
if local {
for _, u := range fr.Module.Units {
u.UnitMeta.Version = LocalVersion
}
for _, pvs := range fr.PackageVersionStates {
pvs.Version = LocalVersion
}
} else {
for _, u := range fr.Module.Units {
// Copy all of ModuleInfo except HasGoMod.
h := u.UnitMeta.ModuleInfo.HasGoMod
u.UnitMeta.ModuleInfo = fr.Module.ModuleInfo
u.UnitMeta.HasGoMod = h
}
for _, pvs := range fr.PackageVersionStates {
pvs.Version = fr.Module.Version
}
}
return fr
}
// proxyFetcher is a test helper function that sets up a test proxy, fetches
// a module using FetchModule, and returns fetch result and a license detector.
func proxyFetcher(t *testing.T, withLicenseDetector bool, ctx context.Context, mod *proxytest.Module, fetchVersion string) (*FetchResult, *licenses.Detector) {
t.Helper()
modulePath := mod.ModulePath
version := mod.Version
if version == "" {
version = sample.VersionString
}
if fetchVersion == "" {
fetchVersion = version
}
proxyClient, teardownProxy := proxytest.SetupTestClient(t, []*proxytest.Module{{
ModulePath: modulePath,
Version: version,
Files: mod.Files,
}})
defer teardownProxy()
got := FetchModule(ctx, modulePath, fetchVersion, NewProxyModuleGetter(proxyClient, source.NewClientForTesting()))
if !withLicenseDetector {
return got, nil
}
d := licenseDetector(ctx, t, modulePath, got.ResolvedVersion, proxyClient)
return got, d
}
// localFetcher is a helper function that creates a test directory to hold a
// module, fetches the module from the directory, and returns a fetch result
// and a license detector.
func localFetcher(t *testing.T, withLicenseDetector bool, ctx context.Context, mod *proxytest.Module, fetchVersion string) (*FetchResult, *licenses.Detector) {
t.Helper()
directory, err := testhelper.CreateTestDirectory(mod.Files)
if err != nil {
t.Fatalf("couldn't create test files")
}
t.Cleanup(func() { os.RemoveAll(directory) })
modulePath := mod.ModulePath
g, err := NewDirectoryModuleGetter(modulePath, directory)
if err != nil {
t.Fatal(err)
}
got := FetchModule(ctx, modulePath, LocalVersion, g)
if !withLicenseDetector {
return got, nil
}
d := licenses.NewDetectorFS(modulePath, LocalVersion, os.DirFS(directory), func(format string, args ...any) {
log.Infof(ctx, format, args...)
})
return got, d
}
func licenseDetector(ctx context.Context, t *testing.T, modulePath, version string, proxyClient *proxy.Client) *licenses.Detector {
t.Helper()
var (
contentDir fs.FS
err error
)
if modulePath == stdlib.ModulePath {
contentDir, _, _, err = stdlib.ContentDir(version)
if err != nil {
t.Fatal(err)
}
} else {
zipReader, err := proxyClient.Zip(ctx, modulePath, version)
if err != nil {
t.Fatal(err)
}
contentDir, err = fs.Sub(zipReader, modulePath+"@"+version)
if err != nil {
t.Fatal(err)
}
}
logf := func(format string, args ...any) {
log.Infof(ctx, format, args...)
}
return licenses.NewDetectorFS(modulePath, version, contentDir, logf)
}
func sortFetchResult(fr *FetchResult) {
sort.Slice(fr.Module.Units, func(i, j int) bool {
return fr.Module.Units[i].Path < fr.Module.Units[j].Path
})
sort.Slice(fr.Module.Licenses, func(i, j int) bool {
return fr.Module.Licenses[i].FilePath < fr.Module.Licenses[j].FilePath
})
sort.Slice(fr.PackageVersionStates, func(i, j int) bool {
return fr.PackageVersionStates[i].PackagePath < fr.PackageVersionStates[j].PackagePath
})
for _, dir := range fr.Module.Units {
sort.Slice(dir.Licenses, func(i, j int) bool {
return dir.Licenses[i].FilePath < dir.Licenses[j].FilePath
})
}
}