internal/postgres: check for excluded in GetNestedModules

When a module path is excluded, we shouldn't display it on the
subdirectories table.

For golang/go#41439

Change-Id: I2033cea50c7957e5dbc98195fed6a54a727866ff
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/256306
Run-TryBot: Julie Qiu <julie@golang.org>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Jonathan Amsterdam <jba@google.com>
Trust: Julie Qiu <julie@golang.org>
diff --git a/internal/postgres/details.go b/internal/postgres/details.go
index 7078fd8..6d03ab3 100644
--- a/internal/postgres/details.go
+++ b/internal/postgres/details.go
@@ -51,7 +51,13 @@
 		if err != nil {
 			return fmt.Errorf("rows.Scan(): %v", err)
 		}
-		modules = append(modules, mi)
+		isExcluded, err := db.IsExcluded(ctx, mi.ModulePath)
+		if err != nil {
+			return err
+		}
+		if !isExcluded {
+			modules = append(modules, mi)
+		}
 		return nil
 	}
 	if err := db.db.RunQuery(ctx, query, collect, modulePath); err != nil {
diff --git a/internal/postgres/details_test.go b/internal/postgres/details_test.go
index 8870883..04c4c2b 100644
--- a/internal/postgres/details_test.go
+++ b/internal/postgres/details_test.go
@@ -21,7 +21,7 @@
 	"golang.org/x/pkgsite/internal/testing/sample"
 )
 
-func TestPostgres_GetNestedModules(t *testing.T) {
+func TestGetNestedModules(t *testing.T) {
 	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
 	defer cancel()
 
@@ -84,6 +84,53 @@
 	}
 }
 
+func TestGetNestedModules_Excluded(t *testing.T) {
+	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
+	defer cancel()
+	defer ResetTestDB(testDB, t)
+
+	test := struct {
+		name            string
+		path            string
+		modules         []*internal.Module
+		wantModulePaths []string
+	}{
+		name: "Nested Modules in cloud.google.com/go that have the same module prefix path",
+		path: "cloud.google.com/go",
+		modules: []*internal.Module{
+			sample.Module("cloud.google.com/go", "v0.46.2", "storage", "spanner", "pubsub"),
+			// cloud.google.com/storage will be excluded below.
+			sample.Module("cloud.google.com/go/storage", "v1.10.0", sample.Suffix),
+			sample.Module("cloud.google.com/go/pubsub", "v1.6.1", sample.Suffix),
+			sample.Module("cloud.google.com/go/spanner", "v1.9.0", sample.Suffix),
+		},
+		wantModulePaths: []string{
+			"cloud.google.com/go/pubsub",
+			"cloud.google.com/go/spanner",
+		},
+	}
+	for _, m := range test.modules {
+		if err := testDB.InsertModule(ctx, m); err != nil {
+			t.Fatal(err)
+		}
+	}
+	if err := testDB.InsertExcludedPrefix(ctx, "cloud.google.com/go/storage", "postgres", "test"); err != nil {
+		t.Fatal(err)
+	}
+
+	gotModules, err := testDB.GetNestedModules(ctx, "cloud.google.com/go")
+	if err != nil {
+		t.Fatal(err)
+	}
+	var gotModulePaths []string
+	for _, mod := range gotModules {
+		gotModulePaths = append(gotModulePaths, mod.ModulePath)
+	}
+	if diff := cmp.Diff(test.wantModulePaths, gotModulePaths); diff != "" {
+		t.Errorf("mismatch (-want +got):\n%s", diff)
+	}
+}
+
 func TestPostgres_GetModuleInfo(t *testing.T) {
 	ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
 	defer cancel()