From f30fd2478e1228a6c9f30bd99bce16d08bffdb73 Mon Sep 17 00:00:00 2001 From: Frank Reyes Date: Fri, 4 Sep 2026 10:52:38 +0200 Subject: [PATCH 1/2] fix(maven): read the version from the Solr "v" field, not "latestVersion" FetchVersions and FetchPackage query the Maven Central search API with core=gav, where each document is one groupId:artifactId:version and the version is in the "v" field. searchDoc.Version was tagged json:"latestVersion" - a field that only exists on the default (non-gav) core, where a document aggregates an artifact - so every FetchVersions result came back with Number == "". The existing tests encode a searchDoc struct into the mock response and decode it with the same struct, so the field name never mattered; added TestFetchVersionsParsesRealSolrGavShape which feeds the raw JSON the API actually returns. Before (against real Solr): guava -> n=150, all Number == "" After: guava -> n=150, Number populated, contains "33.0.0-jre" Co-Authored-By: Claude Sonnet 5 --- internal/maven/maven.go | 14 +++++++++---- internal/maven/maven_test.go | 39 ++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/internal/maven/maven.go b/internal/maven/maven.go index 872a57f..51b2bc1 100644 --- a/internal/maven/maven.go +++ b/internal/maven/maven.go @@ -92,10 +92,16 @@ type searchResponseBody struct { } type searchDoc struct { - ID string `json:"id"` - GroupID string `json:"g"` - ArtifactID string `json:"a"` - Version string `json:"latestVersion"` + ID string `json:"id"` + GroupID string `json:"g"` + ArtifactID string `json:"a"` + // Version is the concrete version of this document. Both FetchPackage + // and FetchVersions query the search API with core=gav, where each doc + // is one groupId:artifactId:version and the version is in the "v" + // field. ("latestVersion" only appears on the default, non-gav core, + // where a doc aggregates an artifact - unmarshalling from that field + // here left every Number empty.) + Version string `json:"v"` Timestamp int64 `json:"timestamp"` VersionCount int `json:"versionCount"` } diff --git a/internal/maven/maven_test.go b/internal/maven/maven_test.go index b4eef91..24c35ae 100644 --- a/internal/maven/maven_test.go +++ b/internal/maven/maven_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "net/http" "net/http/httptest" + "reflect" "testing" "github.com/git-pkgs/registries/internal/core" @@ -141,6 +142,44 @@ func TestFetchVersions(t *testing.T) { } } +// TestFetchVersionsParsesRealSolrGavShape feeds the raw JSON the Maven +// Central search API actually returns for core=gav (version in "v", not +// "latestVersion"). Encoding a searchDoc struct in the other tests hides +// the field name, so this guards the tag directly. +func TestFetchVersionsParsesRealSolrGavShape(t *testing.T) { + const body = `{"responseHeader":{"status":0},"response":{"numFound":3,"start":0,"docs":[ + {"id":"org.slf4j:slf4j-api:2.0.17","g":"org.slf4j","a":"slf4j-api","v":"2.0.17","p":"jar","timestamp":1740501794416}, + {"id":"org.slf4j:slf4j-api:2.0.16","g":"org.slf4j","a":"slf4j-api","v":"2.0.16","p":"jar","timestamp":1725000000000}, + {"id":"org.slf4j:slf4j-api:1.7.36","g":"org.slf4j","a":"slf4j-api","v":"1.7.36","p":"jar","timestamp":1645000000000} + ]}}` + + mux := http.NewServeMux() + mux.HandleFunc("/solrsearch/select", func(w http.ResponseWriter, r *http.Request) { + _, _ = w.Write([]byte(body)) + }) + server := httptest.NewServer(mux) + defer server.Close() + + reg := New(server.URL, core.DefaultClient()) + reg.searchURL = server.URL + + versions, err := reg.FetchVersions(context.Background(), "org.slf4j:slf4j-api") + if err != nil { + t.Fatalf("FetchVersions failed: %v", err) + } + got := make([]string, len(versions)) + for i, v := range versions { + if v.Number == "" { + t.Fatalf("version %d has empty Number - searchDoc is not reading the Solr \"v\" field", i) + } + got[i] = v.Number + } + want := []string{"2.0.17", "2.0.16", "1.7.36"} + if !reflect.DeepEqual(got, want) { + t.Fatalf("versions = %v, want %v", got, want) + } +} + func TestFetchVersionsFallback(t *testing.T) { mux := http.NewServeMux() From e4cbda537a4b33a3cd3c5dfed8e26fc7cdc49638 Mon Sep 17 00:00:00 2001 From: Andrew Nesbitt Date: Fri, 4 Sep 2026 10:27:06 +0100 Subject: [PATCH 2/2] Trim comments on searchDoc.Version and gav-shape test --- internal/maven/maven.go | 7 +------ internal/maven/maven_test.go | 4 ---- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/internal/maven/maven.go b/internal/maven/maven.go index 51b2bc1..f175295 100644 --- a/internal/maven/maven.go +++ b/internal/maven/maven.go @@ -95,12 +95,7 @@ type searchDoc struct { ID string `json:"id"` GroupID string `json:"g"` ArtifactID string `json:"a"` - // Version is the concrete version of this document. Both FetchPackage - // and FetchVersions query the search API with core=gav, where each doc - // is one groupId:artifactId:version and the version is in the "v" - // field. ("latestVersion" only appears on the default, non-gav core, - // where a doc aggregates an artifact - unmarshalling from that field - // here left every Number empty.) + // core=gav returns the version in "v"; "latestVersion" only exists on the non-gav aggregated core. Version string `json:"v"` Timestamp int64 `json:"timestamp"` VersionCount int `json:"versionCount"` diff --git a/internal/maven/maven_test.go b/internal/maven/maven_test.go index 24c35ae..7f684ca 100644 --- a/internal/maven/maven_test.go +++ b/internal/maven/maven_test.go @@ -142,10 +142,6 @@ func TestFetchVersions(t *testing.T) { } } -// TestFetchVersionsParsesRealSolrGavShape feeds the raw JSON the Maven -// Central search API actually returns for core=gav (version in "v", not -// "latestVersion"). Encoding a searchDoc struct in the other tests hides -// the field name, so this guards the tag directly. func TestFetchVersionsParsesRealSolrGavShape(t *testing.T) { const body = `{"responseHeader":{"status":0},"response":{"numFound":3,"start":0,"docs":[ {"id":"org.slf4j:slf4j-api:2.0.17","g":"org.slf4j","a":"slf4j-api","v":"2.0.17","p":"jar","timestamp":1740501794416},