aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEthan Lee <ethanalee@google.com>2026-03-18 20:31:16 +0000
committerGopher Robot <gobot@golang.org>2026-03-25 08:29:54 -0700
commitc57126f9757fb25c781a59cb9ecc97b0af63727a (patch)
tree48b516ffb19fc89d36ed25094c3a8f74a11b754a
parent86d1c7b3f2f2d36dbaf488d847c09e1655356ca7 (diff)
downloadgo-x-pkgsite-c57126f9757fb25c781a59cb9ecc97b0af63727a.tar.xz
all: add LatestVersion to ModuleInfo
- Add LatestVersion field to internal.ModuleInfo and update tests accordingly. - Changed some test formatting to improve readability. Change-Id: I1238e54614ef276d219b31d125cd2cad6b5a66f7 Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/756901 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> kokoro-CI: kokoro <noreply+kokoro@google.com> Auto-Submit: Ethan Lee <ethanalee@google.com> Reviewed-by: Jonathan Amsterdam <jba@google.com>
-rw-r--r--internal/api/api.go1
-rw-r--r--internal/api/api_test.go68
-rw-r--r--internal/discovery.go2
-rw-r--r--internal/latest.go1
-rw-r--r--internal/postgres/details_test.go11
-rw-r--r--internal/postgres/insert_module_test.go4
-rw-r--r--internal/postgres/unit_test.go15
-rw-r--r--internal/postgres/version_test.go17
-rw-r--r--internal/testing/sample/sample.go2
-rw-r--r--internal/worker/fetch_test.go16
-rw-r--r--internal/worker/refetch_test.go7
11 files changed, 125 insertions, 19 deletions
diff --git a/internal/api/api.go b/internal/api/api.go
index d1419c40..4f4fcf7b 100644
--- a/internal/api/api.go
+++ b/internal/api/api.go
@@ -198,6 +198,7 @@ func ServePackage(w http.ResponseWriter, r *http.Request, ds internal.DataSource
ModuleVersion: unit.Version,
Synopsis: synopsis,
IsStandardLibrary: stdlib.Contains(unit.ModulePath),
+ IsLatest: unit.Version == unit.LatestVersion,
GOOS: goos,
GOARCH: goarch,
Docs: docs,
diff --git a/internal/api/api_test.go b/internal/api/api_test.go
index 16a92278..595a3d25 100644
--- a/internal/api/api_test.go
+++ b/internal/api/api_test.go
@@ -30,12 +30,20 @@ func TestServePackage(t *testing.T) {
)
ds.MustInsertModule(ctx, &internal.Module{
- ModuleInfo: internal.ModuleInfo{ModulePath: "example.com", Version: version},
+ ModuleInfo: internal.ModuleInfo{
+ ModulePath: "example.com",
+ Version: version,
+ LatestVersion: "v1.2.4",
+ },
Units: []*internal.Unit{{
UnitMeta: internal.UnitMeta{
- Path: "example.com/pkg",
- ModuleInfo: internal.ModuleInfo{ModulePath: "example.com", Version: version},
- Name: "pkg",
+ Path: "example.com/pkg",
+ ModuleInfo: internal.ModuleInfo{
+ ModulePath: "example.com",
+ Version: version,
+ LatestVersion: "v1.2.4",
+ },
+ Name: "pkg",
},
Documentation: []*internal.Documentation{sample.Documentation("linux", "amd64", sample.DocContents)},
}},
@@ -46,8 +54,9 @@ func TestServePackage(t *testing.T) {
UnitMeta: internal.UnitMeta{
Path: pkgPath,
ModuleInfo: internal.ModuleInfo{
- ModulePath: mp,
- Version: version,
+ ModulePath: mp,
+ Version: version,
+ LatestVersion: version,
},
Name: "b",
},
@@ -61,13 +70,38 @@ func TestServePackage(t *testing.T) {
}
ds.MustInsertModule(ctx, &internal.Module{
ModuleInfo: internal.ModuleInfo{
- ModulePath: mp,
- Version: version,
+ ModulePath: mp,
+ Version: version,
+ LatestVersion: version,
},
Units: []*internal.Unit{u},
})
}
+ ds.MustInsertModule(ctx, &internal.Module{
+ ModuleInfo: internal.ModuleInfo{
+ ModulePath: "example.com",
+ Version: "v1.2.4",
+ LatestVersion: "v1.2.4",
+ },
+ Units: []*internal.Unit{{
+ UnitMeta: internal.UnitMeta{
+ Path: "example.com/pkg",
+ ModuleInfo: internal.ModuleInfo{
+ ModulePath: "example.com",
+ Version: "v1.2.4",
+ LatestVersion: "v1.2.4",
+ },
+ Name: "pkg",
+ },
+ Documentation: []*internal.Documentation{{
+ GOOS: "linux",
+ GOARCH: "amd64",
+ Synopsis: "Basic synopsis",
+ }},
+ }},
+ })
+
for _, test := range []struct {
name string
url string
@@ -83,6 +117,7 @@ func TestServePackage(t *testing.T) {
ModulePath: "example.com",
ModuleVersion: version,
Synopsis: "This is a package synopsis for GOOS=linux, GOARCH=amd64",
+ IsLatest: false,
GOOS: "linux",
GOARCH: "amd64",
},
@@ -109,6 +144,7 @@ func TestServePackage(t *testing.T) {
ModulePath: modulePath1,
ModuleVersion: version,
Synopsis: "Synopsis for " + modulePath1,
+ IsLatest: true,
GOOS: "linux",
GOARCH: "amd64",
},
@@ -122,6 +158,21 @@ func TestServePackage(t *testing.T) {
ModulePath: "example.com",
ModuleVersion: version,
Synopsis: "This is a package synopsis for GOOS=linux, GOARCH=amd64",
+ IsLatest: false,
+ GOOS: "linux",
+ GOARCH: "amd64",
+ },
+ },
+ {
+ name: "latest version",
+ url: "/v1/package/example.com/pkg?version=v1.2.4",
+ wantStatus: http.StatusOK,
+ want: &Package{
+ Path: "example.com/pkg",
+ ModulePath: "example.com",
+ ModuleVersion: "v1.2.4",
+ Synopsis: "Basic synopsis",
+ IsLatest: true,
GOOS: "linux",
GOARCH: "amd64",
},
@@ -161,7 +212,6 @@ func TestServePackage(t *testing.T) {
if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil {
t.Fatalf("json.Unmarshal Package: %v", err)
}
- got.IsLatest = false
if diff := cmp.Diff(want, &got); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
}
diff --git a/internal/discovery.go b/internal/discovery.go
index 16e17d0b..88b48c31 100644
--- a/internal/discovery.go
+++ b/internal/discovery.go
@@ -52,6 +52,8 @@ type ModuleInfo struct {
HasGoMod bool
SourceInfo *source.Info
+ // LatestVersion is the latest good version of the module.
+ LatestVersion string
// Deprecated describes whether the module is deprecated.
Deprecated bool
// DeprecationComment is the comment describing the deprecation, if any.
diff --git a/internal/latest.go b/internal/latest.go
index 14ccf166..8b820b29 100644
--- a/internal/latest.go
+++ b/internal/latest.go
@@ -64,6 +64,7 @@ func isDeprecated(mf *modfile.File) (bool, string) {
// PopulateModuleInfo uses the LatestModuleVersions to populate fields of the given module.
func (li *LatestModuleVersions) PopulateModuleInfo(mi *ModuleInfo) {
+ mi.LatestVersion = li.GoodVersion
mi.Deprecated = li.Deprecated
mi.DeprecationComment = li.deprecationComment
mi.Retracted, mi.RetractionRationale = isRetracted(li.GoModFile, mi.Version)
diff --git a/internal/postgres/details_test.go b/internal/postgres/details_test.go
index 53f55dd2..b2d83d66 100644
--- a/internal/postgres/details_test.go
+++ b/internal/postgres/details_test.go
@@ -180,6 +180,11 @@ func TestPostgres_GetModuleInfo(t *testing.T) {
},
}
+ latestGoodVersions := map[string]string{
+ "mod.1": "v1.1.0",
+ "mod.2": "v1.1.0",
+ }
+
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
for _, v := range test.modules {
@@ -200,7 +205,11 @@ func TestPostgres_GetModuleInfo(t *testing.T) {
t.Fatal("wantIndex too large")
}
wantVI := &test.modules[test.wantIndex].ModuleInfo
- if diff := cmp.Diff(wantVI, gotVI, cmpopts.EquateEmpty(), cmp.AllowUnexported(source.Info{})); diff != "" {
+ wantVI.LatestVersion = latestGoodVersions[wantVI.ModulePath]
+ if diff := cmp.Diff(wantVI, gotVI,
+ cmpopts.EquateEmpty(),
+ cmp.AllowUnexported(source.Info{}),
+ ); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
}
})
diff --git a/internal/postgres/insert_module_test.go b/internal/postgres/insert_module_test.go
index 37dd8b7a..3ae278a7 100644
--- a/internal/postgres/insert_module_test.go
+++ b/internal/postgres/insert_module_test.go
@@ -108,7 +108,9 @@ func checkModule(ctx context.Context, t *testing.T, db *DB, want *internal.Modul
if err != nil {
t.Fatal(err)
}
- if diff := cmp.Diff(want.ModuleInfo, *got, cmp.AllowUnexported(source.Info{})); diff != "" {
+ if diff := cmp.Diff(want.ModuleInfo, *got,
+ cmp.AllowUnexported(source.Info{}),
+ ); diff != "" {
t.Fatalf("testDB.GetModuleInfo(%q, %q) mismatch (-want +got):\n%s", want.ModulePath, want.Version, diff)
}
diff --git a/internal/postgres/unit_test.go b/internal/postgres/unit_test.go
index a35d5658..2decf185 100644
--- a/internal/postgres/unit_test.go
+++ b/internal/postgres/unit_test.go
@@ -92,6 +92,7 @@ func testGetUnitMeta(t *testing.T, ctx context.Context) {
ModulePath: modPath,
Version: version,
IsRedistributable: true,
+ LatestVersion: "v1.1.0",
},
Name: name,
}
@@ -192,6 +193,19 @@ func testGetUnitMeta(t *testing.T, ctx context.Context) {
test.want.Name,
true,
)
+ latestGood := map[string]string{
+ "m.com": "v1.1.0",
+ "m.com/a": "v1.1.0",
+ "m.com/b": "v2.0.0+incompatible",
+ "cloud.google.com/go": "v0.74.0",
+ "cloud.google.com/go/pubsublite": "v0.4.0",
+ "cloud.google.com/go/compute/metadata": "v0.0.0-20181115181204-d50f0e9b2506",
+ }
+ want.LatestVersion = latestGood[want.ModulePath]
+ if want.LatestVersion == "" {
+ // Fallback to version if not in map (e.g. stdlib)
+ want.LatestVersion = want.Version
+ }
want.IsRedistributable = true
want.CommitTime = sample.CommitTime
want.Retracted = test.want.Retracted
@@ -819,6 +833,7 @@ func unitNoLicenses(fullPath, modulePath, version, name string, readme *internal
ModulePath: modulePath,
Version: version,
IsRedistributable: true,
+ LatestVersion: version,
},
Path: fullPath,
Name: name,
diff --git a/internal/postgres/version_test.go b/internal/postgres/version_test.go
index 66c0453e..3f4eaebc 100644
--- a/internal/postgres/version_test.go
+++ b/internal/postgres/version_test.go
@@ -229,6 +229,18 @@ func TestGetVersions(t *testing.T) {
},
}
+ latestVersions := map[string]string{
+ stdlib.ModulePath: "v1.14.6", // v1.15.0-beta.1 is pre-release
+ taggedModuleV3: "v3.1.0", // v3.2.0-beta is pre-release
+ taggedModuleV2: "v2.0.1",
+ taggedAndPseudoModule: "v1.5.2", // v1.5.3-pre1 is pre-release
+ pseudoModule: "v0.0.0-20200101120012-000000000000",
+ otherModule: "v3.0.0",
+ incompatibleModule: "v0.0.0", // v2.0.0+incompatible is incompatible
+ rootModule: "v0.11.6",
+ nestedModule: "v1.0.4",
+ }
+
for _, test := range testCases {
t.Run(test.name, func(t *testing.T) {
for _, w := range test.want {
@@ -237,13 +249,16 @@ func TestGetVersions(t *testing.T) {
w.IsRedistributable = mod.IsRedistributable
w.HasGoMod = mod.HasGoMod
w.SourceInfo = mod.SourceInfo
+ w.LatestVersion = latestVersions[w.ModulePath]
}
got, err := testDB.GetVersionsForPath(ctx, test.path)
if err != nil {
t.Fatal(err)
}
- if diff := cmp.Diff(test.want, got, cmp.AllowUnexported(source.Info{})); diff != "" {
+ if diff := cmp.Diff(test.want, got,
+ cmp.AllowUnexported(source.Info{}),
+ ); diff != "" {
t.Errorf("testDB.GetVersionsForPath(%q) mismatch (-want +got):\n%s", test.path, diff)
}
})
diff --git a/internal/testing/sample/sample.go b/internal/testing/sample/sample.go
index 610dcab2..f08d3bfa 100644
--- a/internal/testing/sample/sample.go
+++ b/internal/testing/sample/sample.go
@@ -273,6 +273,7 @@ func ModuleInfo(modulePath, versionString string) *internal.ModuleInfo {
SourceInfo: source.NewGitHubInfo("https://"+modulePath, "", versionString),
IsRedistributable: true,
HasGoMod: true,
+ LatestVersion: versionString,
}
}
@@ -363,6 +364,7 @@ func UnitMeta(path, modulePath, version, name string, moduleIsRedistributable bo
CommitTime: NowTruncated(),
IsRedistributable: moduleIsRedistributable,
SourceInfo: source.NewGitHubInfo("https://"+modulePath, "", version),
+ LatestVersion: version,
},
}
}
diff --git a/internal/worker/fetch_test.go b/internal/worker/fetch_test.go
index b99ed7ea..5c40ca1f 100644
--- a/internal/worker/fetch_test.go
+++ b/internal/worker/fetch_test.go
@@ -58,6 +58,7 @@ func TestFetchAndUpdateState(t *testing.T) {
CommitTime: testProxyCommitTime,
SourceInfo: source.NewGitHubInfo("https://example.com/multi", "", sample.VersionString),
IsRedistributable: true,
+ LatestVersion: sample.VersionString,
},
Path: "example.com/multi/bar",
Name: "bar",
@@ -114,6 +115,7 @@ func TestFetchAndUpdateState(t *testing.T) {
CommitTime: testProxyCommitTime,
SourceInfo: source.NewGitHubInfo("https://example.com/nonredist", "", sample.VersionString),
IsRedistributable: true,
+ LatestVersion: sample.VersionString,
},
Path: "example.com/nonredist/bar/baz",
Name: "baz",
@@ -144,6 +146,7 @@ func TestFetchAndUpdateState(t *testing.T) {
CommitTime: testProxyCommitTime,
SourceInfo: source.NewGitHubInfo("https://example.com/nonredist", "", sample.VersionString),
IsRedistributable: true,
+ LatestVersion: sample.VersionString,
},
Path: "example.com/nonredist/unk",
Name: "unk",
@@ -168,6 +171,7 @@ func TestFetchAndUpdateState(t *testing.T) {
CommitTime: testProxyCommitTime,
SourceInfo: source.NewGitHubInfo("https://"+buildConstraintsModulePath, "", sample.VersionString),
IsRedistributable: true,
+ LatestVersion: "v1.0.0",
},
Path: buildConstraintsModulePath + "/cpu",
Name: "cpu",
@@ -209,7 +213,10 @@ func TestFetchAndUpdateState(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- if diff := cmp.Diff(test.want.UnitMeta, *got, cmpopts.EquateEmpty(), cmp.AllowUnexported(source.Info{})); diff != "" {
+ if diff := cmp.Diff(test.want.UnitMeta, *got,
+ cmpopts.EquateEmpty(),
+ cmp.AllowUnexported(source.Info{}),
+ ); diff != "" {
t.Fatalf("testDB.GetUnitMeta(ctx, %q, %q) mismatch (-want +got):\n%s", test.modulePath, test.version, diff)
}
@@ -223,10 +230,9 @@ func TestFetchAndUpdateState(t *testing.T) {
if diff := cmp.Diff(test.want, gotPkg,
cmpopts.EquateEmpty(),
cmp.AllowUnexported(source.Info{}),
- cmpopts.IgnoreFields(internal.Unit{}, "Documentation", "BuildContexts"),
- cmpopts.IgnoreFields(internal.Unit{}, "SymbolHistory"),
- cmpopts.IgnoreFields(internal.Unit{}, "Subdirectories")); diff != "" {
- t.Errorf("mismatch on readme (-want +got):\n%s", diff)
+ cmpopts.IgnoreFields(internal.Unit{}, "Documentation", "BuildContexts", "SymbolHistory", "Subdirectories"),
+ ); diff != "" {
+ t.Errorf("mismatch on unit (-want +got):\n%s", diff)
}
if got, want := gotPkg.Documentation, test.want.Documentation; got == nil || want == nil {
if !cmp.Equal(got, want) {
diff --git a/internal/worker/refetch_test.go b/internal/worker/refetch_test.go
index ef4bdc11..d7ef4be4 100644
--- a/internal/worker/refetch_test.go
+++ b/internal/worker/refetch_test.go
@@ -92,6 +92,7 @@ func TestReFetch(t *testing.T) {
CommitTime: time.Date(2019, 1, 30, 0, 0, 0, 0, time.UTC),
IsRedistributable: true,
SourceInfo: source.NewGitHubInfo("https://"+sample.ModulePath, "", sample.VersionString),
+ LatestVersion: version,
},
Path: sample.ModulePath + "/bar",
Name: "bar",
@@ -127,7 +128,8 @@ func TestReFetch(t *testing.T) {
if diff := cmp.Diff(want.UnitMeta, *got,
cmp.AllowUnexported(source.Info{}),
cmpopts.IgnoreFields(licenses.Metadata{}, "Coverage"),
- cmpopts.IgnoreFields(internal.UnitMeta{}, "HasGoMod")); diff != "" {
+ cmpopts.IgnoreFields(internal.UnitMeta{}, "HasGoMod"),
+ ); diff != "" {
t.Fatalf("testDB.GetUnitMeta(ctx, %q, %q) mismatch (-want +got):\n%s", want.ModulePath, want.Version, diff)
}
@@ -139,7 +141,8 @@ func TestReFetch(t *testing.T) {
cmp.AllowUnexported(source.Info{}),
cmpopts.IgnoreFields(internal.Unit{}, "Documentation", "BuildContexts"),
cmpopts.IgnoreFields(licenses.Metadata{}, "Coverage"),
- cmpopts.IgnoreFields(internal.UnitMeta{}, "HasGoMod")); diff != "" {
+ cmpopts.IgnoreFields(internal.UnitMeta{}, "HasGoMod"),
+ ); diff != "" {
t.Errorf("mismatch on readme (-want +got):\n%s", diff)
}
if got, want := gotPkg.Documentation, want.Documentation; got == nil || want == nil {