aboutsummaryrefslogtreecommitdiff
path: root/internal/api/api_test.go
diff options
context:
space:
mode:
authorHana Kim <hyangah@gmail.com>2026-04-12 22:10:16 -0400
committerHyang-Ah Hana Kim <hyangah@gmail.com>2026-04-13 11:42:30 -0700
commitaca48c6d9dcc4892027cdad3243527882201358b (patch)
tree8827e389417a4d7ef4344fd7f28dac0c6be19934 /internal/api/api_test.go
parent70e5087371296e2632232f4a3a795f124c73baf3 (diff)
downloadgo-x-pkgsite-aca48c6d9dcc4892027cdad3243527882201358b.tar.xz
internal/api: improve ambiguous package path resolution
Instead of falling back to UnknownModulePath, we now: 1. Query all candidate module paths. 2. Filter out candidates where the database fell back to a different module path (preventing false positives like google.golang.org). 3. Filter out deprecated or retracted candidates if at least one good candidate exists. 4. Return 400 if ambiguity remains among good candidates. This commit fixes the issue where /v1/package/google.golang.org/adk/agent was returning HTTP 400 because all candidate module paths matched: ``` { "code":400, "message":"ambiguous package path", "candidates":[ {"modulePath":"google.golang.org/adk/agent","packagePath":"google.golang.org/adk/agent"}, {"modulePath":"google.golang.org/adk","packagePath":"google.golang.org/adk/agent"}, {"modulePath":"google.golang.org","packagePath":"google.golang.org/adk/agent"}] } ``` Change-Id: I3ea24bca5144d536490019efd85fb597da214029 Reviewed-on: https://go-review.googlesource.com/c/pkgsite/+/766380 kokoro-CI: kokoro <noreply+kokoro@google.com> Reviewed-by: Jonathan Amsterdam <jba@google.com> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'internal/api/api_test.go')
-rw-r--r--internal/api/api_test.go90
1 files changed, 89 insertions, 1 deletions
diff --git a/internal/api/api_test.go b/internal/api/api_test.go
index 285005de..64b891c7 100644
--- a/internal/api/api_test.go
+++ b/internal/api/api_test.go
@@ -23,6 +23,22 @@ import (
"golang.org/x/pkgsite/internal/vuln"
)
+type fallbackDataSource struct {
+ internal.DataSource
+ fallbackMap map[string]string // requested module -> resolved module
+}
+
+func (ds fallbackDataSource) GetUnitMeta(ctx context.Context, path, requestedModulePath, requestedVersion string) (*internal.UnitMeta, error) {
+ if resolved, ok := ds.fallbackMap[requestedModulePath]; ok {
+ um, err := ds.DataSource.GetUnitMeta(ctx, path, resolved, requestedVersion)
+ if err != nil {
+ return nil, err
+ }
+ return um, nil
+ }
+ return ds.DataSource.GetUnitMeta(ctx, path, requestedModulePath, requestedVersion)
+}
+
func TestServePackage(t *testing.T) {
ctx := context.Background()
ds := fakedatasource.New()
@@ -141,6 +157,7 @@ func TestServePackage(t *testing.T) {
url string
wantStatus int
want any // Can be *Package or *Error
+ overrideDS internal.DataSource
}{
{
name: "basic metadata",
@@ -329,12 +346,83 @@ func TestServePackage(t *testing.T) {
Imports: []string{pkgPath},
},
},
+ {
+ name: "fallback prevention (false positive candidate)",
+ url: "/v1/package/example.com/a/b?version=v1.2.3",
+ wantStatus: http.StatusBadRequest,
+ want: &Error{
+ Code: http.StatusBadRequest,
+ Message: "ambiguous package path",
+ Candidates: []Candidate{
+ {ModulePath: "example.com/a/b", PackagePath: "example.com/a/b"},
+ {ModulePath: "example.com/a", PackagePath: "example.com/a/b"},
+ },
+ },
+ overrideDS: &fallbackDataSource{
+ DataSource: ds,
+ fallbackMap: map[string]string{
+ "example.com": "example.com/a/b", // simulate fallback
+ },
+ },
+ },
+ {
+ name: "deprecation filtering",
+ url: "/v1/package/example.com/a/b?version=v1.2.3",
+ wantStatus: http.StatusOK,
+ want: &Package{
+ Path: pkgPath,
+ ModulePath: modulePath2, // picked because modulePath1 is deprecated
+ ModuleVersion: version,
+ Synopsis: "Synopsis for " + modulePath2,
+ IsLatest: true,
+ GOOS: "linux",
+ GOARCH: "amd64",
+ },
+ overrideDS: func() internal.DataSource {
+ newDS := fakedatasource.New()
+ for _, mp := range []string{modulePath1, modulePath2} {
+ u := &internal.Unit{
+ UnitMeta: internal.UnitMeta{
+ Path: pkgPath,
+ ModuleInfo: internal.ModuleInfo{
+ ModulePath: mp,
+ Version: version,
+ LatestVersion: version,
+ Deprecated: mp == modulePath1,
+ },
+ Name: "b",
+ },
+ Documentation: []*internal.Documentation{
+ {
+ GOOS: "linux",
+ GOARCH: "amd64",
+ Synopsis: "Synopsis for " + mp,
+ },
+ },
+ }
+ newDS.MustInsertModule(ctx, &internal.Module{
+ ModuleInfo: internal.ModuleInfo{
+ ModulePath: mp,
+ Version: version,
+ LatestVersion: version,
+ Deprecated: mp == modulePath1,
+ },
+ Units: []*internal.Unit{u},
+ })
+ }
+ return newDS
+ }(),
+ },
} {
t.Run(test.name, func(t *testing.T) {
r := httptest.NewRequest("GET", test.url, nil)
w := httptest.NewRecorder()
- if err := ServePackage(w, r, ds); err != nil {
+ var currentDS internal.DataSource = ds
+ if test.overrideDS != nil {
+ currentDS = test.overrideDS
+ }
+ if err := ServePackage(w, r, currentDS); err != nil {
ServeError(w, r, err)
}