| // 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() | |
| testDB.MustInsertModule(t, 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 ( | |
| modulePath1 = "deleteme.com" | |
| modulePath2 = "example.com" | |
| ) | |
| initial := []searchDocumentRow{ | |
| {modulePath1 + "/p1", modulePath1, "v0.0.9", 0}, // oldest version of same module | |
| {modulePath1 + "/p2", modulePath1, "v1.1.0", 0}, // older version of same module | |
| {modulePath1 + "/p4", modulePath1, "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+"/")) | |
| db.MustInsertModule(t, 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, modulePath1); 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(modulePath1, "v1.0.0", "p1", "p2") | |
| testDB.MustInsertModule(t, m0) | |
| // Insert another module that imports p2. | |
| mref := sample.Module(modulePath2, "v1.0.0") | |
| u := sample.UnitForPackage(modulePath2+"/ref", modulePath2, mref.Version, "ref", true) | |
| u.Imports = []string{modulePath1 + "/p2"} | |
| sample.AddUnit(mref, u) | |
| testDB.MustInsertModule(t, mref) | |
| // Later version of module does not have p1. | |
| m1 := sample.Module(modulePath1, "v1.1.0", "p2", "p3") | |
| testDB.MustInsertModule(t, 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{ | |
| {modulePath1 + "/p2", modulePath1, "v1.1.0", 1}, | |
| {modulePath1 + "/p3", modulePath1, "v1.1.0", 0}, | |
| {modulePath2 + "/ref", modulePath2, "v1.0.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, nil | |
| } | |
| 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 { | |
| testDB.MustInsertModule(t, sample.Module(sample.ModulePath, v, "")) | |
| } | |
| if err := testDB.DeletePseudoversionsExcept(ctx, sample.ModulePath, pseudo1); err != nil { | |
| t.Fatal(err) | |
| } | |
| mods, _, err := testDB.GetPathVersions(ctx, sample.ModulePath, "", 800, 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 = testDB.GetPathVersions(ctx, sample.ModulePath, "", 10, 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) | |
| } | |
| } |