internal/frontend: refactor building SearchResult

Split out the code that creates a SearchResult from a
postgres.SearchResult, for testing.

Add a test.

Change-Id: Ia1c8b0a69661ef9e4d90f44a93beea276da486b6
Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/347552
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: kokoro <noreply+kokoro@google.com>
Reviewed-by: Julie Qiu <julie@golang.org>
diff --git a/internal/frontend/search.go b/internal/frontend/search.go
index cccebb8..1b58297 100644
--- a/internal/frontend/search.go
+++ b/internal/frontend/search.go
@@ -88,51 +88,7 @@
 
 	var results []*SearchResult
 	for _, r := range dbresults {
-		// For commands, change the name from "main" to the last component of the import path.
-		chipText := ""
-		name := r.Name
-		if name == "main" {
-			chipText = "command"
-			name = effectiveName(r.PackagePath, r.Name)
-		}
-		moduleDesc := "Other packages in module " + r.ModulePath
-		if r.ModulePath == stdlib.ModulePath {
-			moduleDesc = "Related packages in the standard library"
-			chipText = "standard library"
-		}
-		sr := &SearchResult{
-			Name:           name,
-			PackagePath:    r.PackagePath,
-			ModulePath:     r.ModulePath,
-			ChipText:       chipText,
-			Synopsis:       r.Synopsis,
-			DisplayVersion: displayVersion(r.ModulePath, r.Version, r.Version),
-			Licenses:       r.Licenses,
-			CommitTime:     elapsedTime(r.CommitTime),
-			NumImportedBy:  int(r.NumImportedBy),
-			SameModule:     packagePaths(moduleDesc+":", r.SameModule),
-			// Say "other" instead of "lower" because at some point we may
-			// prefer to show a tagged, lower major version over an untagged
-			// higher major version.
-			OtherMajor: modulePaths("Other major versions:", r.OtherMajor),
-		}
-		if searchSymbols {
-			sr.SymbolName = r.SymbolName
-			sr.SymbolKind = strings.ToLower(string(r.SymbolKind))
-			sr.SymbolSynopsis = symbolSynopsis(r)
-			sr.SymbolGOOS = r.SymbolGOOS
-			sr.SymbolGOARCH = r.SymbolGOARCH
-			// If the GOOS is "all" or "linux", it doesn't need to be
-			// specified as a query param. "linux" is the default GOOS when a
-			// package has multiple build contexts, since it is first item
-			// listed in internal.BuildContexts.
-			if r.SymbolGOOS == internal.All || r.SymbolGOOS == "linux" {
-				sr.SymbolLink = fmt.Sprintf("/%s#%s", r.PackagePath, r.SymbolName)
-			} else {
-				sr.SymbolLink = fmt.Sprintf("/%s?GOOS=%s#%s", r.PackagePath, r.SymbolGOOS, r.SymbolName)
-			}
-		}
-		results = append(results, sr)
+		results = append(results, newSearchResult(r, searchSymbols))
 	}
 
 	var (
@@ -168,6 +124,54 @@
 	return sp, nil
 }
 
+func newSearchResult(r *postgres.SearchResult, searchSymbols bool) *SearchResult {
+	// For commands, change the name from "main" to the last component of the import path.
+	chipText := ""
+	name := r.Name
+	if name == "main" {
+		chipText = "command"
+		name = effectiveName(r.PackagePath, r.Name)
+	}
+	moduleDesc := "Other packages in module " + r.ModulePath
+	if r.ModulePath == stdlib.ModulePath {
+		moduleDesc = "Related packages in the standard library"
+		chipText = "standard library"
+	}
+	sr := &SearchResult{
+		Name:           name,
+		PackagePath:    r.PackagePath,
+		ModulePath:     r.ModulePath,
+		ChipText:       chipText,
+		Synopsis:       r.Synopsis,
+		DisplayVersion: displayVersion(r.ModulePath, r.Version, r.Version),
+		Licenses:       r.Licenses,
+		CommitTime:     elapsedTime(r.CommitTime),
+		NumImportedBy:  int(r.NumImportedBy),
+		SameModule:     packagePaths(moduleDesc+":", r.SameModule),
+		// Say "other" instead of "lower" because at some point we may
+		// prefer to show a tagged, lower major version over an untagged
+		// higher major version.
+		OtherMajor: modulePaths("Other major versions:", r.OtherMajor),
+	}
+	if searchSymbols {
+		sr.SymbolName = r.SymbolName
+		sr.SymbolKind = strings.ToLower(string(r.SymbolKind))
+		sr.SymbolSynopsis = symbolSynopsis(r)
+		sr.SymbolGOOS = r.SymbolGOOS
+		sr.SymbolGOARCH = r.SymbolGOARCH
+		// If the GOOS is "all" or "linux", it doesn't need to be
+		// specified as a query param. "linux" is the default GOOS when a
+		// package has multiple build contexts, since it is first item
+		// listed in internal.BuildContexts.
+		if r.SymbolGOOS == internal.All || r.SymbolGOOS == "linux" {
+			sr.SymbolLink = fmt.Sprintf("/%s#%s", r.PackagePath, r.SymbolName)
+		} else {
+			sr.SymbolLink = fmt.Sprintf("/%s?GOOS=%s#%s", r.PackagePath, r.SymbolGOOS, r.SymbolName)
+		}
+	}
+	return sr
+}
+
 func symbolSynopsis(r *postgres.SearchResult) string {
 	switch r.SymbolKind {
 	case internal.SymbolKindField:
diff --git a/internal/frontend/search_test.go b/internal/frontend/search_test.go
index 1d076d1..5a3a45b 100644
--- a/internal/frontend/search_test.go
+++ b/internal/frontend/search_test.go
@@ -243,6 +243,70 @@
 	}
 }
 
+func TestNewSearchResult(t *testing.T) {
+	for _, test := range []struct {
+		name string
+		in   postgres.SearchResult
+		want SearchResult
+	}{
+		{
+			name: "basic",
+			in: postgres.SearchResult{
+				Name:        "pkg",
+				PackagePath: "m.com/pkg",
+				ModulePath:  "m.com",
+				Version:     "v1.0.0",
+			},
+			want: SearchResult{
+				Name:           "pkg",
+				PackagePath:    "m.com/pkg",
+				ModulePath:     "m.com",
+				DisplayVersion: "v1.0.0",
+			},
+		},
+		{
+			name: "command",
+			in: postgres.SearchResult{
+				Name:        "main",
+				PackagePath: "m.com/cmd",
+				ModulePath:  "m.com",
+				Version:     "v1.0.0",
+			},
+			want: SearchResult{
+				Name:           "cmd",
+				PackagePath:    "m.com/cmd",
+				ModulePath:     "m.com",
+				DisplayVersion: "v1.0.0",
+				ChipText:       "command",
+			},
+		},
+		{
+			name: "stdlib",
+			in: postgres.SearchResult{
+				Name:        "math",
+				PackagePath: "math",
+				ModulePath:  "std",
+				Version:     "v1.14.0",
+			},
+			want: SearchResult{
+				Name:           "math",
+				PackagePath:    "math",
+				ModulePath:     "std",
+				DisplayVersion: "go1.14",
+				ChipText:       "standard library",
+			},
+		},
+	} {
+		t.Run(test.name, func(t *testing.T) {
+			got := newSearchResult(&test.in, false)
+			test.want.CommitTime = "Jan  1, 0001"
+			if diff := cmp.Diff(&test.want, got); diff != "" {
+				t.Errorf("mimatch (-want, +got):\n%s", diff)
+			}
+		})
+	}
+}
+
 func TestApproximateNumber(t *testing.T) {
 	tests := []struct {
 		estimate int