From c57126f9757fb25c781a59cb9ecc97b0af63727a Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Wed, 18 Mar 2026 20:31:16 +0000 Subject: 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 kokoro-CI: kokoro Auto-Submit: Ethan Lee Reviewed-by: Jonathan Amsterdam --- internal/api/api.go | 1 + internal/api/api_test.go | 68 ++++++++++++++++++++++++++++----- internal/discovery.go | 2 + internal/latest.go | 1 + internal/postgres/details_test.go | 11 +++++- internal/postgres/insert_module_test.go | 4 +- internal/postgres/unit_test.go | 15 ++++++++ internal/postgres/version_test.go | 17 ++++++++- internal/testing/sample/sample.go | 2 + internal/worker/fetch_test.go | 16 +++++--- internal/worker/refetch_test.go | 7 +++- 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 { -- cgit v1.3