| // 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) |
| } |
| } |