blob: 6652a2408fa20412c555b1072cf662231fca3443 [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 postgres
import (
"context"
"database/sql"
"errors"
"sort"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"golang.org/x/pkgsite/internal/database"
"golang.org/x/pkgsite/internal/derrors"
"golang.org/x/pkgsite/internal/testing/sample"
"golang.org/x/pkgsite/internal/version"
)
func TestDeleteModule(t *testing.T) {
t.Parallel()
testDB, release := acquire(t)
defer release()
ctx := context.Background()
v := sample.DefaultModule()
MustInsertModule(ctx, t, testDB, v)
if _, err := testDB.GetModuleInfo(ctx, v.ModulePath, v.Version); err != nil {
t.Fatal(err)
}
vm := sample.DefaultVersionMap()
if err := testDB.UpsertVersionMap(ctx, vm); err != nil {
t.Fatal(err)
}
if _, err := testDB.GetVersionMap(ctx, v.ModulePath, v.Version); err != nil {
t.Fatal(err)
}
if err := testDB.DeleteModule(ctx, v.ModulePath, v.Version); err != nil {
t.Fatal(err)
}
if _, err := testDB.GetModuleInfo(ctx, v.ModulePath, v.Version); !errors.Is(err, derrors.NotFound) {
t.Errorf("got %v, want NotFound", err)
}
var x int
err := testDB.Underlying().QueryRow(ctx, "SELECT 1 FROM imports_unique WHERE from_module_path = $1",
v.ModulePath).Scan(&x)
if err != sql.ErrNoRows {
t.Errorf("imports_unique: got %v, want ErrNoRows", err)
}
err = testDB.Underlying().QueryRow(
ctx,
"SELECT 1 FROM version_map WHERE module_path = $1 AND resolved_version = $2",
v.ModulePath, v.Version).Scan(&x)
if err != sql.ErrNoRows {
t.Errorf("version_map: got %v, want ErrNoRows", err)
}
}
func TestDeleteFromSearch(t *testing.T) {
t.Parallel()
ctx := context.Background()
const modulePath = "deleteme.com"
initial := []searchDocumentRow{
{modulePath + "/p1", modulePath, "v0.0.9", 0}, // oldest version of same module
{modulePath + "/p2", modulePath, "v1.1.0", 0}, // older version of same module
{modulePath + "/p4", modulePath, "v1.9.0", 0}, // newer version of same module
{"other.org/p2", "other.org", "v1.1.0", 0}, // older version of a different module
}
insertInitial := func(db *DB) {
t.Helper()
for _, r := range initial {
sm := sample.Module(r.ModulePath, r.Version, strings.TrimPrefix(r.PackagePath, r.ModulePath+"/"))
MustInsertModule(ctx, t, db, sm)
}
// Older versions are deleted by InsertModule, so only the newest versions are here.
checkSearchDocuments(ctx, t, db, initial[2:])
}
t.Run("deleteModuleFromSearchDocuments", func(t *testing.T) {
// Non-empty list of packages tested above. This tests an empty list.
testDB, release := acquire(t)
defer release()
insertInitial(testDB)
if err := deleteModuleFromSearchDocuments(ctx, testDB.db, modulePath); err != nil {
t.Fatal(err)
}
checkSearchDocuments(ctx, t, testDB, []searchDocumentRow{
{"other.org/p2", "other.org", "v1.1.0", 0}, // other module not deleted
})
})
t.Run("deleteOtherModulePackagesFromSearchDocuments", func(t *testing.T) {
testDB, release := acquire(t)
defer release()
// Insert a module with two packages.
m0 := sample.Module(modulePath, "v1.0.0", "p1", "p2")
MustInsertModule(ctx, t, testDB, m0)
// Set the imported-by count to a non-zero value so we can tell which
// rows were deleted.
if _, err := testDB.db.Exec(ctx, `UPDATE search_documents SET imported_by_count = 1`); err != nil {
t.Fatal(err)
}
// Later version of module does not have p1.
m1 := sample.Module(modulePath, "v1.1.0", "p2", "p3")
MustInsertModule(ctx, t, testDB, m1)
// p1 should be gone, p2 should be there with the same
// imported_by_count, and p3 should be there with a zero count.
want := []searchDocumentRow{
{modulePath + "/p2", modulePath, "v1.1.0", 1},
{modulePath + "/p3", modulePath, "v1.1.0", 0},
}
checkSearchDocuments(ctx, t, testDB, want)
})
}
type searchDocumentRow struct {
PackagePath, ModulePath, Version string
ImportedByCount int
}
func readSearchDocuments(ctx context.Context, db *DB) ([]searchDocumentRow, error) {
rows, err := database.CollectStructs[searchDocumentRow](ctx, db.db, `SELECT package_path, module_path, version, imported_by_count FROM search_documents`)
if err != nil {
return nil, err
}
return rows, err
}
func checkSearchDocuments(ctx context.Context, t *testing.T, db *DB, want []searchDocumentRow) {
t.Helper()
got, err := readSearchDocuments(ctx, db)
if err != nil {
t.Fatal(err)
}
less := func(r1, r2 searchDocumentRow) bool {
if r1.PackagePath != r2.PackagePath {
return r1.PackagePath < r2.PackagePath
}
if r1.ModulePath != r2.ModulePath {
return r1.ModulePath < r2.ModulePath
}
return r1.Version < r2.Version
}
sort.Slice(got, func(i, j int) bool { return less(got[i], got[j]) })
sort.Slice(want, func(i, j int) bool { return less(want[i], want[j]) })
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("search documents mismatch (-want, +got):\n%s", diff)
}
}
func TestDeletePseudoversionsExcept(t *testing.T) {
t.Parallel()
ctx := context.Background()
testDB, release := acquire(t)
defer release()
pseudo1 := "v0.0.0-20190904010203-89fb59e2e920"
versions := []string{
sample.VersionString,
pseudo1,
"v0.0.0-20190904010203-89fb59e2e920",
"v0.0.0-20190904010203-89fb59e2e920",
}
for _, v := range versions {
MustInsertModule(ctx, t, testDB, sample.Module(sample.ModulePath, v, ""))
}
if err := testDB.DeletePseudoversionsExcept(ctx, sample.ModulePath, pseudo1); err != nil {
t.Fatal(err)
}
mods, err := getPathVersions(ctx, testDB, sample.ModulePath, version.TypeRelease)
if err != nil {
t.Fatal(err)
}
if len(mods) != 1 && mods[0].Version != sample.VersionString {
t.Errorf("module version %q was not found", sample.VersionString)
}
mods, err = getPathVersions(ctx, testDB, sample.ModulePath, version.TypePseudo)
if err != nil {
t.Fatal(err)
}
if len(mods) != 1 {
t.Fatalf("pseudoversions expected to be deleted were not")
}
if mods[0].Version != pseudo1 {
t.Errorf("got %q; want %q", mods[0].Version, pseudo1)
}
}