| // Copyright 2026 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| package api |
| |
| import ( |
| "net/url" |
| "testing" |
| |
| "github.com/google/go-cmp/cmp" |
| ) |
| |
| func TestParseParams(t *testing.T) { |
| type Nested struct { |
| ListParams |
| } |
| type DeepNested struct { |
| Nested |
| } |
| type EmbeddedPtr struct { |
| *ListParams |
| } |
| type extraParams struct { |
| Int64 int64 `form:"i64"` |
| Slice []string `form:"slice"` |
| PtrInt *int `form:"ptr"` |
| PtrPtr **int `form:"ptrptr"` |
| PtrSlice []*string `form:"ptrslice"` |
| private int `form:"private"` // Should be ignored |
| } |
| type overflowParams struct { |
| Int8 int8 `form:"i8"` |
| } |
| type unsupportedParams struct { |
| Float float64 `form:"float"` |
| } |
| |
| for _, test := range []struct { |
| name string |
| values url.Values |
| dst any |
| want any |
| wantErr bool |
| }{ |
| { |
| name: "PackageParams", |
| values: url.Values{"module": {"m"}, "version": {"v1.0.0"}, "goos": {"linux"}, "examples": {"true"}}, |
| dst: &PackageParams{}, |
| want: &PackageParams{ |
| Module: "m", |
| Version: "v1.0.0", |
| GOOS: "linux", |
| Examples: true, |
| }, |
| }, |
| { |
| name: "Boolean presence", |
| values: url.Values{"examples": {"true"}, "licenses": {"1"}}, |
| dst: &PackageParams{}, |
| want: &PackageParams{ |
| Examples: true, |
| Licenses: true, |
| }, |
| }, |
| { |
| name: "Boolean presence (ModuleParams)", |
| values: url.Values{"licenses": {"0"}, "readme": {"false"}}, |
| dst: &ModuleParams{}, |
| want: &ModuleParams{ |
| Licenses: false, |
| Readme: false, |
| }, |
| }, |
| { |
| name: "Empty bool", |
| values: url.Values{"examples": {""}}, |
| dst: &PackageParams{}, |
| want: &PackageParams{ |
| Examples: false, |
| }, |
| }, |
| { |
| name: "Invalid bool (on)", |
| values: url.Values{"examples": {"on"}}, |
| dst: &PackageParams{}, |
| wantErr: true, |
| }, |
| { |
| name: "Deeply nested embedding", |
| values: url.Values{"limit": {"100"}}, |
| dst: &DeepNested{}, |
| want: &DeepNested{ |
| Nested: Nested{ |
| ListParams: ListParams{Limit: 100}, |
| }, |
| }, |
| }, |
| { |
| name: "Extra types (int64, slice, ptr, ptrptr, ptrslice)", |
| values: url.Values{ |
| "i64": {"9223372036854775807"}, |
| "slice": {"a", "b"}, |
| "ptr": {"42"}, |
| "ptrptr": {"84"}, |
| "ptrslice": {"one", "two"}, |
| "private": {"1"}, |
| }, |
| dst: &extraParams{}, |
| want: &extraParams{ |
| Int64: 9223372036854775807, |
| Slice: []string{"a", "b"}, |
| PtrInt: intPtr(42), |
| PtrPtr: intPtrPtr(84), |
| PtrSlice: []*string{stringPtr("one"), stringPtr("two")}, |
| private: 0, // Ignored |
| }, |
| }, |
| { |
| name: "Malformed int", |
| values: url.Values{"limit": {"10.5"}}, |
| dst: &SymbolsParams{}, |
| wantErr: true, |
| }, |
| { |
| name: "Malformed bool", |
| values: url.Values{"examples": {"maybe"}}, |
| dst: &PackageParams{}, |
| wantErr: true, |
| }, |
| { |
| name: "Empty int", |
| values: url.Values{"limit": {""}}, |
| dst: &SymbolsParams{}, |
| wantErr: true, |
| }, |
| { |
| name: "Not a pointer", |
| values: url.Values{}, |
| dst: PackageParams{}, |
| wantErr: true, |
| }, |
| { |
| name: "Int8 overflow", |
| values: url.Values{"i8": {"128"}}, |
| dst: &overflowParams{}, |
| wantErr: true, |
| }, |
| { |
| name: "Unsupported type", |
| values: url.Values{"float": {"1.2"}}, |
| dst: &unsupportedParams{}, |
| wantErr: true, |
| }, |
| { |
| name: "Embedded pointer", |
| values: url.Values{"limit": {"50"}}, |
| dst: &EmbeddedPtr{}, |
| want: &EmbeddedPtr{ |
| ListParams: &ListParams{Limit: 50}, |
| }, |
| }, |
| } { |
| t.Run(test.name, func(t *testing.T) { |
| err := ParseParams(test.values, test.dst) |
| if (err != nil) != test.wantErr { |
| t.Fatalf("ParseParams() error = %v, wantErr %v", err, test.wantErr) |
| } |
| if test.wantErr { |
| return |
| } |
| if diff := cmp.Diff(test.want, test.dst, cmp.AllowUnexported(extraParams{})); diff != "" { |
| t.Errorf("ParseParams() mismatch (-want +got):\n%s", diff) |
| } |
| }) |
| } |
| } |
| |
| func intPtr(i int) *int { return &i } |
| func intPtrPtr(i int) **int { |
| p := intPtr(i) |
| return &p |
| } |
| func stringPtr(s string) *string { return &s } |