blob: b5f49a3dc232e1ca68025a4957603ccb66f0034f [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 frontend
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"golang.org/x/pkgsite/internal"
"golang.org/x/pkgsite/internal/licenses"
"golang.org/x/pkgsite/internal/postgres"
"golang.org/x/pkgsite/internal/testing/sample"
)
func TestFetchSearchPage(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
defer postgres.ResetTestDB(testDB, t)
var (
now = sample.NowTruncated()
moduleFoo = &internal.Module{
ModuleInfo: internal.ModuleInfo{
ModulePath: "github.com/mod/foo",
Version: "v1.0.0",
CommitTime: now,
IsRedistributable: true,
},
Units: []*internal.Unit{
{
UnitMeta: internal.UnitMeta{
Name: "foo",
Path: "github.com/mod/foo",
Licenses: sample.LicenseMetadata,
CommitTime: now,
ModulePath: "github.com/mod/foo",
Version: "v1.0.0",
IsRedistributable: true,
},
Documentation: &internal.Documentation{
Synopsis: "foo is a package.",
},
Readme: &internal.Readme{
Filepath: "readme",
Contents: "readme",
},
},
},
}
moduleBar = &internal.Module{
ModuleInfo: internal.ModuleInfo{
ModulePath: "github.com/mod/bar",
Version: "v1.0.0",
CommitTime: now,
IsRedistributable: true,
},
Units: []*internal.Unit{
{
UnitMeta: internal.UnitMeta{
Name: "bar",
Path: "github.com/mod/bar",
Licenses: sample.LicenseMetadata,
CommitTime: now,
ModulePath: "github.com/mod/bar",
Version: "v1.0.0",
IsRedistributable: true,
},
Documentation: &internal.Documentation{
Synopsis: "bar is used by foo.",
},
Readme: &internal.Readme{
Filepath: "readme",
Contents: "readme",
},
},
},
}
)
for _, m := range []*internal.Module{moduleFoo, moduleBar} {
// TODO(golang/go#39629): Delete once packages table is fully deprecated.
for _, pkg := range m.Packages() {
m.LegacyPackages = append(m.LegacyPackages, &internal.LegacyPackage{
Path: pkg.Path,
Name: pkg.Name,
Synopsis: pkg.Documentation.Synopsis,
Licenses: sample.LicenseMetadata,
IsRedistributable: pkg.IsRedistributable,
GOOS: pkg.Documentation.GOOS,
GOARCH: pkg.Documentation.GOARCH,
})
}
if err := testDB.InsertModule(ctx, m); err != nil {
t.Fatal(err)
}
}
for _, tc := range []struct {
name, query string
modules []*internal.Module
wantSearchPage *SearchPage
}{
{
name: "want expected search page",
query: "foo bar",
wantSearchPage: &SearchPage{
Pagination: pagination{
TotalCount: 1,
ResultCount: 1,
PrevPage: 0,
NextPage: 0,
limit: 20,
Page: 1,
Pages: []int{1},
},
Results: []*SearchResult{
{
Name: moduleBar.Packages()[0].Name,
PackagePath: moduleBar.Packages()[0].Path,
ModulePath: moduleBar.ModulePath,
Synopsis: moduleBar.Packages()[0].Documentation.Synopsis,
DisplayVersion: moduleBar.Version,
Licenses: []string{"MIT"},
CommitTime: elapsedTime(moduleBar.CommitTime),
NumImportedBy: 0,
},
},
},
},
{
name: "want only foo search page",
query: "package",
wantSearchPage: &SearchPage{
Pagination: pagination{
TotalCount: 1,
ResultCount: 1,
PrevPage: 0,
NextPage: 0,
limit: 20,
Page: 1,
Pages: []int{1},
},
Results: []*SearchResult{
{
Name: moduleFoo.Packages()[0].Name,
PackagePath: moduleFoo.Packages()[0].Path,
ModulePath: moduleFoo.ModulePath,
Synopsis: moduleFoo.Packages()[0].Documentation.Synopsis,
DisplayVersion: moduleFoo.Version,
Licenses: []string{"MIT"},
CommitTime: elapsedTime(moduleFoo.CommitTime),
NumImportedBy: 0,
},
},
},
},
} {
t.Run(tc.name, func(t *testing.T) {
got, err := fetchSearchPage(ctx, testDB, tc.query, paginationParams{limit: 20, page: 1})
if err != nil {
t.Fatalf("fetchSearchPage(db, %q): %v", tc.query, err)
}
opts := cmp.Options{
cmp.AllowUnexported(SearchPage{}, pagination{}),
cmpopts.IgnoreFields(licenses.Metadata{}, "FilePath"),
cmpopts.IgnoreFields(pagination{}, "Approximate"),
}
if diff := cmp.Diff(tc.wantSearchPage, got, opts...); diff != "" {
t.Errorf("fetchSearchPage(db, %q) mismatch (-want +got):\n%s", tc.query, diff)
}
})
}
}
func TestApproximateNumber(t *testing.T) {
tests := []struct {
estimate int
sigma float64
want int
}{
{55872, 0.1, 60000},
{55872, 1.0, 100000},
{45872, 1.0, 0},
{85872, 0.1, 90000},
{85872, 0.4, 100000},
{15711, 0.1, 16000},
{136368, 0.05, 140000},
{136368, 0.005, 136000},
{3, 0.1, 3},
}
for _, test := range tests {
if got := approximateNumber(test.estimate, test.sigma); got != test.want {
t.Errorf("approximateNumber(%d, %f) = %d, want %d", test.estimate, test.sigma, got, test.want)
}
}
}
func TestSearchRequestRedirectPath(t *testing.T) {
// Experiments need to be set in the context, for DB work, and as
// a middleware, for request handling.
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
defer cancel()
defer postgres.ResetTestDB(testDB, t)
golangTools := sample.LegacyModule("golang.org/x/tools", sample.VersionString, "internal/lsp")
std := sample.LegacyModule("std", sample.VersionString,
"cmd/go", "cmd/go/internal/auth", "fmt")
modules := []*internal.Module{golangTools, std}
for _, v := range modules {
if err := testDB.InsertModule(ctx, v); err != nil {
t.Fatal(err)
}
}
for _, tc := range []struct {
name string
query string
want string
}{
{"module", "golang.org/x/tools", "/mod/golang.org/x/tools"},
{"directory", "golang.org/x/tools/internal", "/golang.org/x/tools/internal"},
{"package", "golang.org/x/tools/internal/lsp", "/golang.org/x/tools/internal/lsp"},
{"stdlib package does not redirect", "errors", ""},
{"stdlib package does redirect", "cmd/go", "/cmd/go"},
{"stdlib directory does redirect", "cmd/go/internal", "/cmd/go/internal"},
{"std does not redirect", "std", ""},
{"non-existent path does not redirect", "github.com/non-existent", ""},
{"trim URL scheme from query", "https://golang.org/x/tools", "/mod/golang.org/x/tools"},
} {
t.Run(tc.name, func(t *testing.T) {
if got := searchRequestRedirectPath(ctx, testDB, tc.query); got != tc.want {
t.Errorf("searchRequestRedirectPath(ctx, %q) = %q; want = %q", tc.query, got, tc.want)
}
})
}
}