gddo-server: hide internal packages

List internal packages only underneath their direct parent
or whether is browsing the internal package themselves.

Change-Id: I856178b427de2486c17b8f162e64be62bded9272
Reviewed-on: https://go-review.googlesource.com/90855
Reviewed-by: Tuo Shan <shantuo@google.com>
diff --git a/gddo-server/main.go b/gddo-server/main.go
index 223b838..67accfa 100644
--- a/gddo-server/main.go
+++ b/gddo-server/main.go
@@ -313,7 +313,7 @@
 
 		return s.templates.execute(resp, template, status, http.Header{"Etag": {etag}}, map[string]interface{}{
 			"flashMessages": flashMessages,
-			"pkgs":          pkgs,
+			"pkgs":          removeInternal(pdoc, pkgs),
 			"pdoc":          newTDoc(s.v, pdoc),
 			"importerCount": importerCount,
 		})
@@ -1010,3 +1010,28 @@
 	http.Handle("/", s)
 	log.Fatal(http.ListenAndServe(s.v.GetString(ConfigBindAddress), s))
 }
+
+// removeInternal removes the internal packages from the given package
+// listing unless they are direct children of the given pdoc.
+// Packages filtered by this function will only list internal packages
+// underneath their own package godoc.
+func removeInternal(pdoc *doc.Package, pkgs []database.Package) []database.Package {
+	const internalPkg = "internal"
+
+	if len(pkgs) == 0 {
+		return pkgs
+	}
+	var filtered []database.Package
+	for _, pkg := range pkgs {
+		// List internal packages only under their parent package.
+		// Always list children of the internal packages if user
+		// is looking at the internal godoc.
+		if pdoc.Name != internalPkg && strings.Contains(pkg.Path, internalPkg) {
+			if !strings.HasPrefix(pkg.Path, pdoc.ImportPath+"/"+internalPkg) {
+				continue
+			}
+		}
+		filtered = append(filtered, pkg)
+	}
+	return filtered
+}
diff --git a/gddo-server/main_test.go b/gddo-server/main_test.go
index 7c4bc78..6d42906 100644
--- a/gddo-server/main_test.go
+++ b/gddo-server/main_test.go
@@ -7,7 +7,11 @@
 package main
 
 import (
+	"reflect"
 	"testing"
+
+	"github.com/golang/gddo/database"
+	"github.com/golang/gddo/doc"
 )
 
 var robotTests = []string{
@@ -33,3 +37,77 @@
 		}
 	}
 }
+
+func TestRemoveInternalPkgs(t *testing.T) {
+	tests := []struct {
+		name string
+		pdoc *doc.Package
+		pkgs []database.Package
+		want []database.Package
+	}{
+		{
+			name: "no children",
+			pdoc: &doc.Package{
+				ImportPath: "github.com/user/repo",
+				Name:       "repo",
+			},
+			pkgs: []database.Package{},
+			want: []database.Package{},
+		},
+		{
+			name: "indirect internal children",
+			pdoc: &doc.Package{
+				ImportPath: "github.com/user/repo",
+				Name:       "repo",
+			},
+			pkgs: []database.Package{
+				{Name: "agent", Path: "github.com/user/repo/cmd/internal/agent"},
+				{Name: "agent", Path: "github.com/user/repo/cmd/internal/reporter"},
+				{Name: "tool", Path: "github.com/user/repo/cmd/tool"},
+			},
+			want: []database.Package{
+				{Name: "tool", Path: "github.com/user/repo/cmd/tool"},
+			},
+		},
+		{
+			name: "direct internal children",
+			pdoc: &doc.Package{
+				ImportPath: "github.com/user/repo",
+				Name:       "repo",
+			},
+			pkgs: []database.Package{
+				{Name: "agent", Path: "github.com/user/repo/internal/agent"},
+				{Name: "agent", Path: "github.com/user/repo/internal/reporter"},
+				{Name: "tool", Path: "github.com/user/repo/cmd/tool"},
+			},
+			want: []database.Package{
+				{Name: "agent", Path: "github.com/user/repo/internal/agent"},
+				{Name: "agent", Path: "github.com/user/repo/internal/reporter"},
+				{Name: "tool", Path: "github.com/user/repo/cmd/tool"},
+			},
+		},
+		{
+			name: "internal package",
+			pdoc: &doc.Package{
+				ImportPath: "github.com/user/repo/internal",
+				Name:       "internal",
+			},
+			pkgs: []database.Package{
+				{Name: "agent", Path: "github.com/user/repo/internal/agent"},
+				{Name: "tool", Path: "github.com/user/repo/internal/tool"},
+			},
+			want: []database.Package{
+				{Name: "agent", Path: "github.com/user/repo/internal/agent"},
+				{Name: "tool", Path: "github.com/user/repo/internal/tool"},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, want := removeInternal(tt.pdoc, tt.pkgs), tt.want
+			if !reflect.DeepEqual(got, want) {
+				t.Errorf("removeInternal() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}