blob: 6d3252475417fc410723d026e855d0d512e51dd8 [file] [log] [blame]
// 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 }