diff options
| author | Hana Kim <hyangah@gmail.com> | 2026-04-12 22:10:16 -0400 |
|---|---|---|
| committer | Hyang-Ah Hana Kim <hyangah@gmail.com> | 2026-04-13 11:42:30 -0700 |
| commit | aca48c6d9dcc4892027cdad3243527882201358b (patch) | |
| tree | 8827e389417a4d7ef4344fd7f28dac0c6be19934 /internal/api/api_test.go | |
| parent | 70e5087371296e2632232f4a3a795f124c73baf3 (diff) | |
| download | go-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.go | 90 |
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) } |
