| // 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 postgres |
| |
| import ( |
| "context" |
| "errors" |
| "sort" |
| "testing" |
| |
| "github.com/google/go-cmp/cmp" |
| "github.com/google/go-cmp/cmp/cmpopts" |
| "golang.org/x/pkgsite/internal" |
| "golang.org/x/pkgsite/internal/derrors" |
| "golang.org/x/pkgsite/internal/experiment" |
| "golang.org/x/pkgsite/internal/licenses" |
| "golang.org/x/pkgsite/internal/source" |
| "golang.org/x/pkgsite/internal/stdlib" |
| "golang.org/x/pkgsite/internal/testing/sample" |
| ) |
| |
| func TestLegacyGetDirectory(t *testing.T) { |
| ctx, cancel := context.WithTimeout(context.Background(), testTimeout) |
| defer cancel() |
| |
| defer ResetTestDB(testDB, t) |
| |
| InsertSampleDirectoryTree(ctx, t, testDB) |
| |
| for _, tc := range []struct { |
| name, dirPath, modulePath, version, wantModulePath, wantVersion string |
| wantSuffixes []string |
| wantNotFoundErr bool |
| }{ |
| { |
| name: "latest with ambigious module path, should match longest module path", |
| dirPath: "github.com/hashicorp/vault/api", |
| modulePath: internal.UnknownModulePath, |
| version: internal.LatestVersion, |
| wantVersion: "v1.1.2", |
| wantModulePath: "github.com/hashicorp/vault/api", |
| wantSuffixes: []string{""}, |
| }, |
| { |
| name: "specified version with ambigious module path, should match longest module path", |
| dirPath: "github.com/hashicorp/vault/api", |
| modulePath: internal.UnknownModulePath, |
| version: "v1.1.2", |
| wantVersion: "v1.1.2", |
| wantModulePath: "github.com/hashicorp/vault/api", |
| wantSuffixes: []string{""}, |
| }, |
| { |
| name: "specified version with ambigous module path, but only shorter module path matches for specified version", |
| dirPath: "github.com/hashicorp/vault/api", |
| modulePath: internal.UnknownModulePath, |
| version: "v1.0.3", |
| wantVersion: "v1.0.3", |
| wantModulePath: "github.com/hashicorp/vault", |
| wantSuffixes: []string{"api"}, |
| }, |
| { |
| name: "specified version with ambiguous module path, two module versions exist, but only shorter module path contains matching package", |
| dirPath: "github.com/hashicorp/vault/builtin/audit", |
| modulePath: internal.UnknownModulePath, |
| version: "v1.1.2", |
| wantVersion: "v1.1.2", |
| wantModulePath: "github.com/hashicorp/vault", |
| wantSuffixes: []string{ |
| "builtin/audit/file", |
| "builtin/audit/socket", |
| }, |
| }, |
| { |
| name: "specified module path and version, should match specified shorter module path", |
| dirPath: "github.com/hashicorp/vault/api", |
| modulePath: "github.com/hashicorp/vault", |
| version: "v1.0.3", |
| wantVersion: "v1.0.3", |
| wantModulePath: "github.com/hashicorp/vault", |
| wantSuffixes: []string{"api"}, |
| }, |
| { |
| name: "directory path is the module path at latest", |
| dirPath: "github.com/hashicorp/vault", |
| modulePath: "github.com/hashicorp/vault", |
| version: internal.LatestVersion, |
| wantVersion: "v1.2.3", |
| wantModulePath: "github.com/hashicorp/vault", |
| wantSuffixes: []string{ |
| "internal/foo", |
| "builtin/audit/file", |
| "builtin/audit/socket", |
| "vault/replication", |
| "vault/seal/transit", |
| }, |
| }, |
| { |
| name: "directory path is the module path with specified version", |
| dirPath: "github.com/hashicorp/vault", |
| modulePath: "github.com/hashicorp/vault", |
| version: "v1.0.3", |
| wantVersion: "v1.0.3", |
| wantModulePath: "github.com/hashicorp/vault", |
| wantSuffixes: []string{ |
| "api", |
| "builtin/audit/file", |
| "builtin/audit/socket", |
| }, |
| }, |
| { |
| name: "directory path is a package path", |
| dirPath: "github.com/hashicorp/vault", |
| modulePath: "github.com/hashicorp/vault", |
| version: "v1.0.3", |
| wantVersion: "v1.0.3", |
| wantModulePath: "github.com/hashicorp/vault", |
| wantSuffixes: []string{ |
| "api", |
| "builtin/audit/file", |
| "builtin/audit/socket", |
| }, |
| }, |
| { |
| name: "valid directory path with package at version, no module path", |
| dirPath: "github.com/hashicorp/vault/builtin", |
| modulePath: internal.UnknownModulePath, |
| wantModulePath: "github.com/hashicorp/vault", |
| version: "v1.0.3", |
| wantVersion: "v1.0.3", |
| wantSuffixes: []string{ |
| "builtin/audit/file", |
| "builtin/audit/socket", |
| }, |
| }, |
| { |
| name: "valid directory path with package, specified version and module path", |
| dirPath: "github.com/hashicorp/vault/builtin", |
| modulePath: "github.com/hashicorp/vault", |
| wantModulePath: "github.com/hashicorp/vault", |
| version: "v1.0.3", |
| wantVersion: "v1.0.3", |
| wantSuffixes: []string{ |
| "builtin/audit/file", |
| "builtin/audit/socket", |
| }, |
| }, |
| { |
| name: "latest version of github.com/hashicorp/vault/api in github.com/hashicorp/vault", |
| dirPath: "github.com/hashicorp/vault/api", |
| modulePath: "github.com/hashicorp/vault", |
| version: internal.LatestVersion, |
| wantModulePath: "github.com/hashicorp/vault", |
| wantVersion: "v1.1.2", |
| wantSuffixes: []string{ |
| "api", |
| }, |
| }, |
| { |
| name: "latest version of github.com/hashicorp/vault/api in github.com/hashicorp/vault/api", |
| dirPath: "github.com/hashicorp/vault/api", |
| modulePath: "github.com/hashicorp/vault/api", |
| version: internal.LatestVersion, |
| wantModulePath: "github.com/hashicorp/vault/api", |
| wantVersion: "v1.1.2", |
| wantSuffixes: []string{""}, |
| }, |
| { |
| name: "latest version of internal directory in github.com/hashicorp/vault", |
| dirPath: "github.com/hashicorp/vault/internal", |
| modulePath: internal.UnknownModulePath, |
| version: internal.LatestVersion, |
| wantModulePath: "github.com/hashicorp/vault", |
| wantVersion: "v1.2.3", |
| wantSuffixes: []string{"internal/foo"}, |
| }, |
| { |
| name: "invalid directory, incomplete last element", |
| dirPath: "github.com/hashicorp/vault/builti", |
| modulePath: internal.UnknownModulePath, |
| version: "v1.0.3", |
| wantNotFoundErr: true, |
| }, |
| { |
| name: "stdlib directory", |
| dirPath: "archive", |
| modulePath: stdlib.ModulePath, |
| version: internal.LatestVersion, |
| wantModulePath: stdlib.ModulePath, |
| wantVersion: "v1.13.4", |
| wantSuffixes: []string{ |
| "archive/zip", |
| "archive/tar", |
| }, |
| }, |
| { |
| name: "stdlib package", |
| dirPath: "archive/zip", |
| modulePath: stdlib.ModulePath, |
| version: internal.LatestVersion, |
| wantModulePath: stdlib.ModulePath, |
| wantVersion: "v1.13.4", |
| wantSuffixes: []string{ |
| "archive/zip", |
| }, |
| }, |
| { |
| name: "stdlib package - incomplete last element", |
| dirPath: "archive/zi", |
| modulePath: stdlib.ModulePath, |
| version: internal.LatestVersion, |
| wantNotFoundErr: true, |
| }, |
| { |
| name: "stdlib - internal directory", |
| dirPath: "cmd/internal", |
| modulePath: stdlib.ModulePath, |
| version: internal.LatestVersion, |
| wantModulePath: stdlib.ModulePath, |
| wantVersion: "v1.13.4", |
| wantSuffixes: []string{ |
| "cmd/internal/obj", |
| "cmd/internal/obj/arm", |
| "cmd/internal/obj/arm64", |
| }, |
| }, |
| { |
| name: "stdlib - directory nested within an internal directory", |
| dirPath: "cmd/internal/obj", |
| modulePath: stdlib.ModulePath, |
| version: internal.LatestVersion, |
| wantModulePath: stdlib.ModulePath, |
| wantVersion: "v1.13.4", |
| wantSuffixes: []string{ |
| "cmd/internal/obj", |
| "cmd/internal/obj/arm", |
| "cmd/internal/obj/arm64", |
| }, |
| }, |
| } { |
| t.Run(tc.name, func(t *testing.T) { |
| got, err := testDB.LegacyGetDirectory(ctx, tc.dirPath, tc.modulePath, tc.version, internal.AllFields) |
| if tc.wantNotFoundErr { |
| if !errors.Is(err, derrors.NotFound) { |
| t.Fatalf("want %v; got = \n%+v, %v", derrors.NotFound, got, err) |
| } |
| return |
| } |
| if err != nil { |
| t.Fatal(err) |
| } |
| |
| mi := sample.LegacyModuleInfo(tc.wantModulePath, tc.wantVersion) |
| var wantPackages []*internal.LegacyPackage |
| for _, suffix := range tc.wantSuffixes { |
| pkg := sample.LegacyPackage(tc.wantModulePath, suffix) |
| pkg.Imports = nil |
| wantPackages = append(wantPackages, pkg) |
| } |
| sort.Slice(wantPackages, func(i, j int) bool { |
| return wantPackages[i].Path < wantPackages[j].Path |
| }) |
| |
| wantDirectory := &internal.LegacyDirectory{ |
| LegacyModuleInfo: *mi, |
| Packages: wantPackages, |
| Path: tc.dirPath, |
| } |
| opts := []cmp.Option{ |
| cmpopts.EquateEmpty(), |
| cmp.AllowUnexported(source.Info{}), |
| // The packages table only includes partial license information; it omits the Coverage field. |
| cmpopts.IgnoreFields(licenses.Metadata{}, "Coverage"), |
| } |
| if diff := cmp.Diff(wantDirectory, got, opts...); diff != "" { |
| t.Errorf("testDB.LegacyGetDirectory(ctx, %q, %q, %q) mismatch (-want +got):\n%s", tc.dirPath, tc.modulePath, tc.version, diff) |
| } |
| }) |
| } |
| } |
| |
| func TestGetDirectoryNew(t *testing.T) { |
| ctx, cancel := context.WithTimeout(context.Background(), testTimeout) |
| defer cancel() |
| ctx = experiment.NewContext(ctx, |
| experiment.NewSet(map[string]bool{ |
| internal.ExperimentInsertDirectories: true})) |
| |
| defer ResetTestDB(testDB, t) |
| InsertSampleDirectoryTree(ctx, t, testDB) |
| |
| // Add a module that has READMEs in a directory and a package. |
| m := sample.Module("a.com/m", "v1.2.3", "dir/p") |
| d := findDirectory(m, "a.com/m/dir") |
| d.Readme = &internal.Readme{ |
| Filepath: "DIR_README.md", |
| Contents: "dir readme", |
| } |
| d = findDirectory(m, "a.com/m/dir/p") |
| d.Readme = &internal.Readme{ |
| Filepath: "PKG_README.md", |
| Contents: "pkg readme", |
| } |
| if err := testDB.InsertModule(ctx, m); err != nil { |
| t.Fatal(err) |
| } |
| |
| newVdir := func(path, modulePath, version string, readme *internal.Readme, pkg *internal.PackageNew) *internal.VersionedDirectory { |
| return &internal.VersionedDirectory{ |
| ModuleInfo: *sample.ModuleInfo(modulePath, version), |
| DirectoryNew: internal.DirectoryNew{ |
| DirectoryMeta: internal.DirectoryMeta{ |
| Path: path, |
| V1Path: path, |
| IsRedistributable: true, |
| Licenses: sample.LicenseMetadata, |
| }, |
| Readme: readme, |
| Package: pkg, |
| }, |
| } |
| } |
| |
| newPackage := func(name, path string) *internal.PackageNew { |
| return &internal.PackageNew{ |
| Name: name, |
| Path: path, |
| Documentation: &internal.Documentation{ |
| Synopsis: sample.Synopsis, |
| HTML: sample.DocumentationHTML, |
| GOOS: sample.GOOS, |
| GOARCH: sample.GOARCH, |
| }, |
| Imports: sample.Imports, |
| } |
| } |
| |
| for _, tc := range []struct { |
| name, dirPath, modulePath, version string |
| want *internal.VersionedDirectory |
| wantNotFoundErr bool |
| }{ |
| { |
| name: "module path", |
| dirPath: "github.com/hashicorp/vault", |
| modulePath: "github.com/hashicorp/vault", |
| version: "v1.0.3", |
| want: newVdir("github.com/hashicorp/vault", "github.com/hashicorp/vault", "v1.0.3", |
| &internal.Readme{ |
| Filepath: sample.ReadmeFilePath, |
| Contents: sample.ReadmeContents, |
| }, nil), |
| }, |
| { |
| name: "package path", |
| dirPath: "github.com/hashicorp/vault/api", |
| modulePath: "github.com/hashicorp/vault", |
| version: "v1.0.3", |
| want: newVdir("github.com/hashicorp/vault/api", "github.com/hashicorp/vault", "v1.0.3", nil, |
| newPackage("api", "github.com/hashicorp/vault/api")), |
| }, |
| { |
| name: "directory path", |
| dirPath: "github.com/hashicorp/vault/builtin", |
| modulePath: "github.com/hashicorp/vault", |
| version: "v1.0.3", |
| want: newVdir("github.com/hashicorp/vault/builtin", "github.com/hashicorp/vault", "v1.0.3", nil, nil), |
| }, |
| { |
| name: "stdlib directory", |
| dirPath: "archive", |
| modulePath: stdlib.ModulePath, |
| version: "v1.13.4", |
| want: newVdir("archive", stdlib.ModulePath, "v1.13.4", nil, nil), |
| }, |
| { |
| name: "stdlib package", |
| dirPath: "archive/zip", |
| modulePath: stdlib.ModulePath, |
| version: "v1.13.4", |
| want: newVdir("archive/zip", stdlib.ModulePath, "v1.13.4", nil, newPackage("zip", "archive/zip")), |
| }, |
| { |
| name: "stdlib package - incomplete last element", |
| dirPath: "archive/zi", |
| modulePath: stdlib.ModulePath, |
| version: "v1.13.4", |
| wantNotFoundErr: true, |
| }, |
| { |
| name: "stdlib - internal directory", |
| dirPath: "cmd/internal", |
| modulePath: stdlib.ModulePath, |
| version: "v1.13.4", |
| want: newVdir("cmd/internal", stdlib.ModulePath, "v1.13.4", nil, nil), |
| }, |
| { |
| name: "directory with readme", |
| dirPath: "a.com/m/dir", |
| modulePath: "a.com/m", |
| version: "v1.2.3", |
| want: newVdir("a.com/m/dir", "a.com/m", "v1.2.3", &internal.Readme{ |
| Filepath: "DIR_README.md", |
| Contents: "dir readme", |
| }, nil), |
| }, |
| { |
| name: "package with readme", |
| dirPath: "a.com/m/dir/p", |
| modulePath: "a.com/m", |
| version: "v1.2.3", |
| want: newVdir("a.com/m/dir/p", "a.com/m", "v1.2.3", |
| &internal.Readme{ |
| Filepath: "PKG_README.md", |
| Contents: "pkg readme", |
| }, |
| newPackage("p", "a.com/m/dir/p")), |
| }, |
| } { |
| t.Run(tc.name, func(t *testing.T) { |
| got, err := testDB.GetDirectoryNew(ctx, tc.dirPath, tc.modulePath, tc.version) |
| if tc.wantNotFoundErr { |
| if !errors.Is(err, derrors.NotFound) { |
| t.Fatalf("want %v; got = \n%+v, %v", derrors.NotFound, got, err) |
| } |
| return |
| } |
| if err != nil { |
| t.Fatal(err) |
| } |
| opts := []cmp.Option{ |
| cmp.AllowUnexported(source.Info{}), |
| // The packages table only includes partial license information; it omits the Coverage field. |
| cmpopts.IgnoreFields(licenses.Metadata{}, "Coverage"), |
| } |
| // TODO(golang/go#38513): remove once we start displaying |
| // READMEs for directories instead of the top-level module. |
| tc.want.Readme = &internal.Readme{ |
| Filepath: sample.ReadmeFilePath, |
| Contents: sample.ReadmeContents, |
| } |
| if diff := cmp.Diff(tc.want, got, opts...); diff != "" { |
| t.Errorf("mismatch (-want, +got):\n%s", diff) |
| } |
| }) |
| } |
| } |
| |
| func findDirectory(m *internal.Module, path string) *internal.DirectoryNew { |
| for _, d := range m.Directories { |
| if d.Path == path { |
| return d |
| } |
| } |
| return nil |
| } |
| |
| func TestLegacyGetDirectoryFieldSet(t *testing.T) { |
| ctx, cancel := context.WithTimeout(context.Background(), testTimeout) |
| defer cancel() |
| |
| defer ResetTestDB(testDB, t) |
| |
| m := sample.Module("m.c", sample.VersionString, "d/p") |
| m.LegacyPackages[0].Imports = nil |
| if err := testDB.InsertModule(ctx, m); err != nil { |
| t.Fatal(err) |
| } |
| |
| got, err := testDB.LegacyGetDirectory(ctx, "m.c/d", "m.c", sample.VersionString, internal.MinimalFields) |
| if err != nil { |
| t.Fatal(err) |
| } |
| if g, w := got.Packages[0].DocumentationHTML, internal.StringFieldMissing; g != w { |
| t.Errorf("DocumentationHTML = %q, want %q", g, w) |
| } |
| } |