mirror of
https://github.com/labstack/echo.git
synced 2024-12-24 20:14:31 +02:00
9b0e63046b
* Fluent Binder for Query/Path/Form binding. * CI: report coverage for latest go (1.15) version * improve docs, remove uncommented code * separate unixtime with sec and nanosec precision binding
2758 lines
78 KiB
Go
2758 lines
78 KiB
Go
// run tests as external package to get real feel for API
|
|
package echo
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/stretchr/testify/assert"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func createTestContext(URL string, body io.Reader, pathParams map[string]string) Context {
|
|
e := New()
|
|
req := httptest.NewRequest(http.MethodGet, URL, body)
|
|
if body != nil {
|
|
req.Header.Set(HeaderContentType, MIMEApplicationJSON)
|
|
}
|
|
rec := httptest.NewRecorder()
|
|
c := e.NewContext(req, rec)
|
|
|
|
if len(pathParams) > 0 {
|
|
names := make([]string, 0)
|
|
values := make([]string, 0)
|
|
for name, value := range pathParams {
|
|
names = append(names, name)
|
|
values = append(values, value)
|
|
}
|
|
c.SetParamNames(names...)
|
|
c.SetParamValues(values...)
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
func TestBindingError_Error(t *testing.T) {
|
|
err := NewBindingError("id", []string{"1", "nope"}, "bind failed", errors.New("internal error"))
|
|
assert.EqualError(t, err, `code=400, message=bind failed, internal=internal error, field=id`)
|
|
|
|
bErr := err.(*BindingError)
|
|
assert.Equal(t, 400, bErr.Code)
|
|
assert.Equal(t, "bind failed", bErr.Message)
|
|
assert.Equal(t, errors.New("internal error"), bErr.Internal)
|
|
|
|
assert.Equal(t, "id", bErr.Field)
|
|
assert.Equal(t, []string{"1", "nope"}, bErr.Values)
|
|
}
|
|
|
|
func TestBindingError_ErrorJSON(t *testing.T) {
|
|
err := NewBindingError("id", []string{"1", "nope"}, "bind failed", errors.New("internal error"))
|
|
|
|
resp, err := json.Marshal(err)
|
|
|
|
assert.Equal(t, `{"field":"id","message":"bind failed"}`, string(resp))
|
|
}
|
|
|
|
func TestPathParamsBinder(t *testing.T) {
|
|
c := createTestContext("/api/user/999", nil, map[string]string{
|
|
"id": "1",
|
|
"nr": "2",
|
|
"slice": "3",
|
|
})
|
|
b := PathParamsBinder(c)
|
|
|
|
id := int64(99)
|
|
nr := int64(88)
|
|
var slice = make([]int64, 0)
|
|
var notExisting = make([]int64, 0)
|
|
err := b.Int64("id", &id).
|
|
Int64("nr", &nr).
|
|
Int64s("slice", &slice).
|
|
Int64s("not_existing", ¬Existing).
|
|
BindError()
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(1), id)
|
|
assert.Equal(t, int64(2), nr)
|
|
assert.Equal(t, []int64{3}, slice) // binding params to slice does not make sense but it should not panic either
|
|
assert.Equal(t, []int64{}, notExisting) // binding params to slice does not make sense but it should not panic either
|
|
}
|
|
|
|
func TestQueryParamsBinder_FailFast(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
whenURL string
|
|
givenFailFast bool
|
|
expectError []string
|
|
}{
|
|
{
|
|
name: "ok, FailFast=true stops at first error",
|
|
whenURL: "/api/user/999?nr=en&id=nope",
|
|
givenFailFast: true,
|
|
expectError: []string{
|
|
`code=400, message=failed to bind field value to int64, internal=strconv.ParseInt: parsing "nope": invalid syntax, field=id`,
|
|
},
|
|
},
|
|
{
|
|
name: "ok, FailFast=false encounters all errors",
|
|
whenURL: "/api/user/999?nr=en&id=nope",
|
|
givenFailFast: false,
|
|
expectError: []string{
|
|
`code=400, message=failed to bind field value to int64, internal=strconv.ParseInt: parsing "nope": invalid syntax, field=id`,
|
|
`code=400, message=failed to bind field value to int64, internal=strconv.ParseInt: parsing "en": invalid syntax, field=nr`,
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, map[string]string{"id": "999"})
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
id := int64(99)
|
|
nr := int64(88)
|
|
errs := b.Int64("id", &id).
|
|
Int64("nr", &nr).
|
|
BindErrors()
|
|
|
|
assert.Len(t, errs, len(tc.expectError))
|
|
for _, err := range errs {
|
|
assert.Contains(t, tc.expectError, err.Error())
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestFormFieldBinder(t *testing.T) {
|
|
e := New()
|
|
body := `texta=foo&slice=5`
|
|
req := httptest.NewRequest(http.MethodPost, "/api/search?id=1&nr=2&slice=3&slice=4", strings.NewReader(body))
|
|
req.Header.Set(HeaderContentLength, strconv.Itoa(len(body)))
|
|
req.Header.Set(HeaderContentType, MIMEApplicationForm)
|
|
|
|
rec := httptest.NewRecorder()
|
|
c := e.NewContext(req, rec)
|
|
|
|
b := FormFieldBinder(c)
|
|
|
|
var texta string
|
|
id := int64(99)
|
|
nr := int64(88)
|
|
var slice = make([]int64, 0)
|
|
var notExisting = make([]int64, 0)
|
|
err := b.
|
|
Int64s("slice", &slice).
|
|
Int64("id", &id).
|
|
Int64("nr", &nr).
|
|
String("texta", &texta).
|
|
Int64s("notExisting", ¬Existing).
|
|
BindError()
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "foo", texta)
|
|
assert.Equal(t, int64(1), id)
|
|
assert.Equal(t, int64(2), nr)
|
|
assert.Equal(t, []int64{5, 3, 4}, slice)
|
|
assert.Equal(t, []int64{}, notExisting)
|
|
}
|
|
|
|
func TestValueBinder_errorStopsBinding(t *testing.T) {
|
|
// this test documents "feature" that binding multiple params can change destination if it was binded before
|
|
// failing parameter binding
|
|
|
|
c := createTestContext("/api/user/999?id=1&nr=nope", nil, nil)
|
|
b := QueryParamsBinder(c)
|
|
|
|
id := int64(99) // will be changed before nr binding fails
|
|
nr := int64(88) // will not be changed
|
|
err := b.Int64("id", &id).
|
|
Int64("nr", &nr).
|
|
BindError()
|
|
|
|
assert.EqualError(t, err, "code=400, message=failed to bind field value to int64, internal=strconv.ParseInt: parsing \"nope\": invalid syntax, field=nr")
|
|
assert.Equal(t, int64(1), id)
|
|
assert.Equal(t, int64(88), nr)
|
|
}
|
|
|
|
func TestValueBinder_BindError(t *testing.T) {
|
|
c := createTestContext("/api/user/999?nr=en&id=nope", nil, nil)
|
|
b := QueryParamsBinder(c)
|
|
|
|
id := int64(99)
|
|
nr := int64(88)
|
|
err := b.Int64("id", &id).
|
|
Int64("nr", &nr).
|
|
BindError()
|
|
|
|
assert.EqualError(t, err, "code=400, message=failed to bind field value to int64, internal=strconv.ParseInt: parsing \"nope\": invalid syntax, field=id")
|
|
assert.Nil(t, b.errors)
|
|
assert.Nil(t, b.BindError())
|
|
}
|
|
|
|
func TestValueBinder_GetValues(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
whenValuesFunc func(sourceParam string) []string
|
|
expect []int64
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, default implementation",
|
|
expect: []int64{1, 101},
|
|
},
|
|
{
|
|
name: "ok, values returns nil",
|
|
whenValuesFunc: func(sourceParam string) []string {
|
|
return nil
|
|
},
|
|
expect: []int64(nil),
|
|
},
|
|
{
|
|
name: "ok, values returns empty slice",
|
|
whenValuesFunc: func(sourceParam string) []string {
|
|
return []string{}
|
|
},
|
|
expect: []int64(nil),
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext("/search?nr=en&id=1&id=101", nil, nil)
|
|
b := QueryParamsBinder(c)
|
|
if tc.whenValuesFunc != nil {
|
|
b.ValuesFunc = tc.whenValuesFunc
|
|
}
|
|
|
|
var IDs []int64
|
|
err := b.Int64s("id", &IDs).BindError()
|
|
|
|
assert.Equal(t, tc.expect, IDs)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_CustomFuncWithError(t *testing.T) {
|
|
c := createTestContext("/search?nr=en&id=1&id=101", nil, nil)
|
|
b := QueryParamsBinder(c)
|
|
|
|
id := int64(99)
|
|
givenCustomFunc := func(values []string) []error {
|
|
assert.Equal(t, []string{"1", "101"}, values)
|
|
|
|
return []error{
|
|
errors.New("first error"),
|
|
errors.New("second error"),
|
|
}
|
|
}
|
|
err := b.CustomFunc("id", givenCustomFunc).BindError()
|
|
|
|
assert.Equal(t, int64(99), id)
|
|
assert.EqualError(t, err, "first error")
|
|
}
|
|
|
|
func TestValueBinder_CustomFunc(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenFuncErrors []error
|
|
whenURL string
|
|
expectParamValues []string
|
|
expectValue interface{}
|
|
expectErrors []string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?nr=en&id=1&id=100",
|
|
expectParamValues: []string{"1", "100"},
|
|
expectValue: int64(1000),
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nr=en",
|
|
expectParamValues: []string{},
|
|
expectValue: int64(99),
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?nr=en&id=1&id=100",
|
|
expectParamValues: []string{"1", "100"},
|
|
expectValue: int64(99),
|
|
expectErrors: []string{"previous error"},
|
|
},
|
|
{
|
|
name: "nok, func returns errors",
|
|
givenFuncErrors: []error{
|
|
errors.New("first error"),
|
|
errors.New("second error"),
|
|
},
|
|
whenURL: "/search?nr=en&id=1&id=100",
|
|
expectParamValues: []string{"1", "100"},
|
|
expectValue: int64(99),
|
|
expectErrors: []string{"first error", "second error"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
id := int64(99)
|
|
givenCustomFunc := func(values []string) []error {
|
|
assert.Equal(t, tc.expectParamValues, values)
|
|
if tc.givenFuncErrors == nil {
|
|
id = 1000 // emulated conversion and setting value
|
|
return nil
|
|
}
|
|
return tc.givenFuncErrors
|
|
}
|
|
errs := b.CustomFunc("id", givenCustomFunc).BindErrors()
|
|
|
|
assert.Equal(t, tc.expectValue, id)
|
|
if tc.expectErrors != nil {
|
|
assert.Len(t, errs, len(tc.expectErrors))
|
|
for _, err := range errs {
|
|
assert.Contains(t, tc.expectErrors, err.Error())
|
|
}
|
|
} else {
|
|
assert.Nil(t, errs)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_MustCustomFunc(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenFuncErrors []error
|
|
whenURL string
|
|
expectParamValues []string
|
|
expectValue interface{}
|
|
expectErrors []string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?nr=en&id=1&id=100",
|
|
expectParamValues: []string{"1", "100"},
|
|
expectValue: int64(1000),
|
|
},
|
|
{
|
|
name: "nok, params values empty, returns error, value is not changed",
|
|
whenURL: "/search?nr=en",
|
|
expectParamValues: []string{},
|
|
expectValue: int64(99),
|
|
expectErrors: []string{"code=400, message=required field value is empty, field=id"},
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?nr=en&id=1&id=100",
|
|
expectParamValues: []string{"1", "100"},
|
|
expectValue: int64(99),
|
|
expectErrors: []string{"previous error"},
|
|
},
|
|
{
|
|
name: "nok, func returns errors",
|
|
givenFuncErrors: []error{
|
|
errors.New("first error"),
|
|
errors.New("second error"),
|
|
},
|
|
whenURL: "/search?nr=en&id=1&id=100",
|
|
expectParamValues: []string{"1", "100"},
|
|
expectValue: int64(99),
|
|
expectErrors: []string{"first error", "second error"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
id := int64(99)
|
|
givenCustomFunc := func(values []string) []error {
|
|
assert.Equal(t, tc.expectParamValues, values)
|
|
if tc.givenFuncErrors == nil {
|
|
id = 1000 // emulated conversion and setting value
|
|
return nil
|
|
}
|
|
return tc.givenFuncErrors
|
|
}
|
|
errs := b.MustCustomFunc("id", givenCustomFunc).BindErrors()
|
|
|
|
assert.Equal(t, tc.expectValue, id)
|
|
if tc.expectErrors != nil {
|
|
assert.Len(t, errs, len(tc.expectErrors))
|
|
for _, err := range errs {
|
|
assert.Contains(t, tc.expectErrors, err.Error())
|
|
}
|
|
} else {
|
|
assert.Nil(t, errs)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_String(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue string
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=en¶m=de",
|
|
expectValue: "en",
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nr=en",
|
|
expectValue: "default",
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?nr=en&id=1&id=100",
|
|
expectValue: "default",
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=en¶m=de",
|
|
expectValue: "en",
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nr=en",
|
|
expectValue: "default",
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?nr=en&id=1&id=100",
|
|
expectValue: "default",
|
|
expectError: "previous error",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
dest := "default"
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustString("param", &dest).BindError()
|
|
} else {
|
|
err = b.String("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Strings(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue []string
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=en¶m=de",
|
|
expectValue: []string{"en", "de"},
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nr=en",
|
|
expectValue: []string{"default"},
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?nr=en&id=1&id=100",
|
|
expectValue: []string{"default"},
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=en¶m=de",
|
|
expectValue: []string{"en", "de"},
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nr=en",
|
|
expectValue: []string{"default"},
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?nr=en&id=1&id=100",
|
|
expectValue: []string{"default"},
|
|
expectError: "previous error",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
dest := []string{"default"}
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustStrings("param", &dest).BindError()
|
|
} else {
|
|
err = b.Strings("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Int64_intValue(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue int64
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: 1,
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: 99,
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: 99,
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok, conversion fails, value is not changed",
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: 99,
|
|
expectError: "code=400, message=failed to bind field value to int64, internal=strconv.ParseInt: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: 1,
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: 99,
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: 99,
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok (must), conversion fails, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: 99,
|
|
expectError: "code=400, message=failed to bind field value to int64, internal=strconv.ParseInt: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
dest := int64(99)
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustInt64("param", &dest).BindError()
|
|
} else {
|
|
err = b.Int64("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Int_errorMessage(t *testing.T) {
|
|
// int/uint (without byte size) has a little bit different error message so test these separately
|
|
c := createTestContext("/search?param=nope", nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(false)
|
|
|
|
destInt := 99
|
|
destUint := uint(98)
|
|
errs := b.Int("param", &destInt).Uint("param", &destUint).BindErrors()
|
|
|
|
assert.Equal(t, 99, destInt)
|
|
assert.Equal(t, uint(98), destUint)
|
|
assert.EqualError(t, errs[0], `code=400, message=failed to bind field value to int, internal=strconv.ParseInt: parsing "nope": invalid syntax, field=param`)
|
|
assert.EqualError(t, errs[1], `code=400, message=failed to bind field value to uint, internal=strconv.ParseUint: parsing "nope": invalid syntax, field=param`)
|
|
}
|
|
|
|
func TestValueBinder_Uint64_uintValue(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue uint64
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: 1,
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: 99,
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: 99,
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok, conversion fails, value is not changed",
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: 99,
|
|
expectError: "code=400, message=failed to bind field value to uint64, internal=strconv.ParseUint: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: 1,
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: 99,
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: 99,
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok (must), conversion fails, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: 99,
|
|
expectError: "code=400, message=failed to bind field value to uint64, internal=strconv.ParseUint: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
dest := uint64(99)
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustUint64("param", &dest).BindError()
|
|
} else {
|
|
err = b.Uint64("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Int_Types(t *testing.T) {
|
|
type target struct {
|
|
int64 int64
|
|
mustInt64 int64
|
|
uint64 uint64
|
|
mustUint64 uint64
|
|
|
|
int32 int32
|
|
mustInt32 int32
|
|
uint32 uint32
|
|
mustUint32 uint32
|
|
|
|
int16 int16
|
|
mustInt16 int16
|
|
uint16 uint16
|
|
mustUint16 uint16
|
|
|
|
int8 int8
|
|
mustInt8 int8
|
|
uint8 uint8
|
|
mustUint8 uint8
|
|
|
|
byte byte
|
|
mustByte byte
|
|
|
|
int int
|
|
mustInt int
|
|
uint uint
|
|
mustUint uint
|
|
}
|
|
types := []string{
|
|
"int64=1",
|
|
"mustInt64=2",
|
|
"uint64=3",
|
|
"mustUint64=4",
|
|
|
|
"int32=5",
|
|
"mustInt32=6",
|
|
"uint32=7",
|
|
"mustUint32=8",
|
|
|
|
"int16=9",
|
|
"mustInt16=10",
|
|
"uint16=11",
|
|
"mustUint16=12",
|
|
|
|
"int8=13",
|
|
"mustInt8=14",
|
|
"uint8=15",
|
|
"mustUint8=16",
|
|
|
|
"byte=17",
|
|
"mustByte=18",
|
|
|
|
"int=19",
|
|
"mustInt=20",
|
|
"uint=21",
|
|
"mustUint=22",
|
|
}
|
|
c := createTestContext("/search?"+strings.Join(types, "&"), nil, nil)
|
|
b := QueryParamsBinder(c)
|
|
|
|
dest := target{}
|
|
err := b.
|
|
Int64("int64", &dest.int64).
|
|
MustInt64("mustInt64", &dest.mustInt64).
|
|
Uint64("uint64", &dest.uint64).
|
|
MustUint64("mustUint64", &dest.mustUint64).
|
|
Int32("int32", &dest.int32).
|
|
MustInt32("mustInt32", &dest.mustInt32).
|
|
Uint32("uint32", &dest.uint32).
|
|
MustUint32("mustUint32", &dest.mustUint32).
|
|
Int16("int16", &dest.int16).
|
|
MustInt16("mustInt16", &dest.mustInt16).
|
|
Uint16("uint16", &dest.uint16).
|
|
MustUint16("mustUint16", &dest.mustUint16).
|
|
Int8("int8", &dest.int8).
|
|
MustInt8("mustInt8", &dest.mustInt8).
|
|
Uint8("uint8", &dest.uint8).
|
|
MustUint8("mustUint8", &dest.mustUint8).
|
|
Byte("byte", &dest.byte).
|
|
MustByte("mustByte", &dest.mustByte).
|
|
Int("int", &dest.int).
|
|
MustInt("mustInt", &dest.mustInt).
|
|
Uint("uint", &dest.uint).
|
|
MustUint("mustUint", &dest.mustUint).
|
|
BindError()
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(1), dest.int64)
|
|
assert.Equal(t, int64(2), dest.mustInt64)
|
|
assert.Equal(t, uint64(3), dest.uint64)
|
|
assert.Equal(t, uint64(4), dest.mustUint64)
|
|
|
|
assert.Equal(t, int32(5), dest.int32)
|
|
assert.Equal(t, int32(6), dest.mustInt32)
|
|
assert.Equal(t, uint32(7), dest.uint32)
|
|
assert.Equal(t, uint32(8), dest.mustUint32)
|
|
|
|
assert.Equal(t, int16(9), dest.int16)
|
|
assert.Equal(t, int16(10), dest.mustInt16)
|
|
assert.Equal(t, uint16(11), dest.uint16)
|
|
assert.Equal(t, uint16(12), dest.mustUint16)
|
|
|
|
assert.Equal(t, int8(13), dest.int8)
|
|
assert.Equal(t, int8(14), dest.mustInt8)
|
|
assert.Equal(t, uint8(15), dest.uint8)
|
|
assert.Equal(t, uint8(16), dest.mustUint8)
|
|
|
|
assert.Equal(t, uint8(17), dest.byte)
|
|
assert.Equal(t, uint8(18), dest.mustByte)
|
|
|
|
assert.Equal(t, 19, dest.int)
|
|
assert.Equal(t, 20, dest.mustInt)
|
|
assert.Equal(t, uint(21), dest.uint)
|
|
assert.Equal(t, uint(22), dest.mustUint)
|
|
}
|
|
|
|
func TestValueBinder_Int64s_intsValue(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue []int64
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=1¶m=2¶m=1",
|
|
expectValue: []int64{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []int64{99},
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []int64{99},
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok, conversion fails, value is not changed",
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: []int64{99},
|
|
expectError: "code=400, message=failed to bind field value to int64, internal=strconv.ParseInt: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=2¶m=1",
|
|
expectValue: []int64{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []int64{99},
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []int64{99},
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok (must), conversion fails, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: []int64{99},
|
|
expectError: "code=400, message=failed to bind field value to int64, internal=strconv.ParseInt: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
dest := []int64{99} // when values are set with bind - contents before bind is gone
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustInt64s("param", &dest).BindError()
|
|
} else {
|
|
err = b.Int64s("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Uint64s_uintsValue(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue []uint64
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=1¶m=2¶m=1",
|
|
expectValue: []uint64{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []uint64{99},
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []uint64{99},
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok, conversion fails, value is not changed",
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: []uint64{99},
|
|
expectError: "code=400, message=failed to bind field value to uint64, internal=strconv.ParseUint: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=2¶m=1",
|
|
expectValue: []uint64{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []uint64{99},
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []uint64{99},
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok (must), conversion fails, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: []uint64{99},
|
|
expectError: "code=400, message=failed to bind field value to uint64, internal=strconv.ParseUint: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
dest := []uint64{99} // when values are set with bind - contents before bind is gone
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustUint64s("param", &dest).BindError()
|
|
} else {
|
|
err = b.Uint64s("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Ints_Types(t *testing.T) {
|
|
type target struct {
|
|
int64 []int64
|
|
mustInt64 []int64
|
|
uint64 []uint64
|
|
mustUint64 []uint64
|
|
|
|
int32 []int32
|
|
mustInt32 []int32
|
|
uint32 []uint32
|
|
mustUint32 []uint32
|
|
|
|
int16 []int16
|
|
mustInt16 []int16
|
|
uint16 []uint16
|
|
mustUint16 []uint16
|
|
|
|
int8 []int8
|
|
mustInt8 []int8
|
|
uint8 []uint8
|
|
mustUint8 []uint8
|
|
|
|
int []int
|
|
mustInt []int
|
|
uint []uint
|
|
mustUint []uint
|
|
}
|
|
types := []string{
|
|
"int64=1",
|
|
"mustInt64=2",
|
|
"uint64=3",
|
|
"mustUint64=4",
|
|
|
|
"int32=5",
|
|
"mustInt32=6",
|
|
"uint32=7",
|
|
"mustUint32=8",
|
|
|
|
"int16=9",
|
|
"mustInt16=10",
|
|
"uint16=11",
|
|
"mustUint16=12",
|
|
|
|
"int8=13",
|
|
"mustInt8=14",
|
|
"uint8=15",
|
|
"mustUint8=16",
|
|
|
|
"int=19",
|
|
"mustInt=20",
|
|
"uint=21",
|
|
"mustUint=22",
|
|
}
|
|
url := "/search?"
|
|
for _, v := range types {
|
|
url = url + "&" + v + "&" + v
|
|
}
|
|
c := createTestContext(url, nil, nil)
|
|
b := QueryParamsBinder(c)
|
|
|
|
dest := target{}
|
|
err := b.
|
|
Int64s("int64", &dest.int64).
|
|
MustInt64s("mustInt64", &dest.mustInt64).
|
|
Uint64s("uint64", &dest.uint64).
|
|
MustUint64s("mustUint64", &dest.mustUint64).
|
|
Int32s("int32", &dest.int32).
|
|
MustInt32s("mustInt32", &dest.mustInt32).
|
|
Uint32s("uint32", &dest.uint32).
|
|
MustUint32s("mustUint32", &dest.mustUint32).
|
|
Int16s("int16", &dest.int16).
|
|
MustInt16s("mustInt16", &dest.mustInt16).
|
|
Uint16s("uint16", &dest.uint16).
|
|
MustUint16s("mustUint16", &dest.mustUint16).
|
|
Int8s("int8", &dest.int8).
|
|
MustInt8s("mustInt8", &dest.mustInt8).
|
|
Uint8s("uint8", &dest.uint8).
|
|
MustUint8s("mustUint8", &dest.mustUint8).
|
|
Ints("int", &dest.int).
|
|
MustInts("mustInt", &dest.mustInt).
|
|
Uints("uint", &dest.uint).
|
|
MustUints("mustUint", &dest.mustUint).
|
|
BindError()
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, []int64{1, 1}, dest.int64)
|
|
assert.Equal(t, []int64{2, 2}, dest.mustInt64)
|
|
assert.Equal(t, []uint64{3, 3}, dest.uint64)
|
|
assert.Equal(t, []uint64{4, 4}, dest.mustUint64)
|
|
|
|
assert.Equal(t, []int32{5, 5}, dest.int32)
|
|
assert.Equal(t, []int32{6, 6}, dest.mustInt32)
|
|
assert.Equal(t, []uint32{7, 7}, dest.uint32)
|
|
assert.Equal(t, []uint32{8, 8}, dest.mustUint32)
|
|
|
|
assert.Equal(t, []int16{9, 9}, dest.int16)
|
|
assert.Equal(t, []int16{10, 10}, dest.mustInt16)
|
|
assert.Equal(t, []uint16{11, 11}, dest.uint16)
|
|
assert.Equal(t, []uint16{12, 12}, dest.mustUint16)
|
|
|
|
assert.Equal(t, []int8{13, 13}, dest.int8)
|
|
assert.Equal(t, []int8{14, 14}, dest.mustInt8)
|
|
assert.Equal(t, []uint8{15, 15}, dest.uint8)
|
|
assert.Equal(t, []uint8{16, 16}, dest.mustUint8)
|
|
|
|
assert.Equal(t, []int{19, 19}, dest.int)
|
|
assert.Equal(t, []int{20, 20}, dest.mustInt)
|
|
assert.Equal(t, []uint{21, 21}, dest.uint)
|
|
assert.Equal(t, []uint{22, 22}, dest.mustUint)
|
|
}
|
|
|
|
func TestValueBinder_Ints_Types_FailFast(t *testing.T) {
|
|
// FailFast() should stop parsing and return early
|
|
errTmpl := "code=400, message=failed to bind field value to %v, internal=strconv.Parse%v: parsing \"nope\": invalid syntax, field=param"
|
|
c := createTestContext("/search?param=1¶m=nope¶m=2", nil, nil)
|
|
|
|
var dest64 []int64
|
|
err := QueryParamsBinder(c).FailFast(true).Int64s("param", &dest64).BindError()
|
|
assert.Equal(t, []int64(nil), dest64)
|
|
assert.EqualError(t, err, fmt.Sprintf(errTmpl, "int64", "Int"))
|
|
|
|
var dest32 []int32
|
|
err = QueryParamsBinder(c).FailFast(true).Int32s("param", &dest32).BindError()
|
|
assert.Equal(t, []int32(nil), dest32)
|
|
assert.EqualError(t, err, fmt.Sprintf(errTmpl, "int32", "Int"))
|
|
|
|
var dest16 []int16
|
|
err = QueryParamsBinder(c).FailFast(true).Int16s("param", &dest16).BindError()
|
|
assert.Equal(t, []int16(nil), dest16)
|
|
assert.EqualError(t, err, fmt.Sprintf(errTmpl, "int16", "Int"))
|
|
|
|
var dest8 []int8
|
|
err = QueryParamsBinder(c).FailFast(true).Int8s("param", &dest8).BindError()
|
|
assert.Equal(t, []int8(nil), dest8)
|
|
assert.EqualError(t, err, fmt.Sprintf(errTmpl, "int8", "Int"))
|
|
|
|
var dest []int
|
|
err = QueryParamsBinder(c).FailFast(true).Ints("param", &dest).BindError()
|
|
assert.Equal(t, []int(nil), dest)
|
|
assert.EqualError(t, err, fmt.Sprintf(errTmpl, "int", "Int"))
|
|
|
|
var destu64 []uint64
|
|
err = QueryParamsBinder(c).FailFast(true).Uint64s("param", &destu64).BindError()
|
|
assert.Equal(t, []uint64(nil), destu64)
|
|
assert.EqualError(t, err, fmt.Sprintf(errTmpl, "uint64", "Uint"))
|
|
|
|
var destu32 []uint32
|
|
err = QueryParamsBinder(c).FailFast(true).Uint32s("param", &destu32).BindError()
|
|
assert.Equal(t, []uint32(nil), destu32)
|
|
assert.EqualError(t, err, fmt.Sprintf(errTmpl, "uint32", "Uint"))
|
|
|
|
var destu16 []uint16
|
|
err = QueryParamsBinder(c).FailFast(true).Uint16s("param", &destu16).BindError()
|
|
assert.Equal(t, []uint16(nil), destu16)
|
|
assert.EqualError(t, err, fmt.Sprintf(errTmpl, "uint16", "Uint"))
|
|
|
|
var destu8 []uint8
|
|
err = QueryParamsBinder(c).FailFast(true).Uint8s("param", &destu8).BindError()
|
|
assert.Equal(t, []uint8(nil), destu8)
|
|
assert.EqualError(t, err, fmt.Sprintf(errTmpl, "uint8", "Uint"))
|
|
|
|
var destu []uint
|
|
err = QueryParamsBinder(c).FailFast(true).Uints("param", &destu).BindError()
|
|
assert.Equal(t, []uint(nil), destu)
|
|
assert.EqualError(t, err, fmt.Sprintf(errTmpl, "uint", "Uint"))
|
|
}
|
|
|
|
func TestValueBinder_Bool(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue bool
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=true¶m=1",
|
|
expectValue: true,
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: false,
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: false,
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok, conversion fails, value is not changed",
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: false,
|
|
expectError: "code=400, message=failed to bind field value to bool, internal=strconv.ParseBool: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: true,
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: false,
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: false,
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok (must), conversion fails, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: false,
|
|
expectError: "code=400, message=failed to bind field value to bool, internal=strconv.ParseBool: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
dest := false
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustBool("param", &dest).BindError()
|
|
} else {
|
|
err = b.Bool("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Bools(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue []bool
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=true¶m=false¶m=1¶m=0",
|
|
expectValue: []bool{true, false, true, false},
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []bool(nil),
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
givenBindErrors: []error{errors.New("previous error")},
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []bool(nil),
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok, conversion fails, value is not changed",
|
|
whenURL: "/search?param=true¶m=nope¶m=100",
|
|
expectValue: []bool(nil),
|
|
expectError: "code=400, message=failed to bind field value to bool, internal=strconv.ParseBool: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "nok, conversion fails fast, value is not changed",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=true¶m=nope¶m=100",
|
|
expectValue: []bool(nil),
|
|
expectError: "code=400, message=failed to bind field value to bool, internal=strconv.ParseBool: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=true¶m=false¶m=1¶m=0",
|
|
expectValue: []bool{true, false, true, false},
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []bool(nil),
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
givenBindErrors: []error{errors.New("previous error")},
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []bool(nil),
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok (must), conversion fails, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: []bool(nil),
|
|
expectError: "code=400, message=failed to bind field value to bool, internal=strconv.ParseBool: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
b.errors = tc.givenBindErrors
|
|
|
|
var dest []bool
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustBools("param", &dest).BindError()
|
|
} else {
|
|
err = b.Bools("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Float64(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue float64
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=4.3¶m=1",
|
|
expectValue: 4.3,
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: 1.123,
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: 1.123,
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok, conversion fails, value is not changed",
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: 1.123,
|
|
expectError: "code=400, message=failed to bind field value to float64, internal=strconv.ParseFloat: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=4.3¶m=100",
|
|
expectValue: 4.3,
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: 1.123,
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: 1.123,
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok (must), conversion fails, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: 1.123,
|
|
expectError: "code=400, message=failed to bind field value to float64, internal=strconv.ParseFloat: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
dest := 1.123
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustFloat64("param", &dest).BindError()
|
|
} else {
|
|
err = b.Float64("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Float64s(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue []float64
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=4.3¶m=0",
|
|
expectValue: []float64{4.3, 0},
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []float64(nil),
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
givenBindErrors: []error{errors.New("previous error")},
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []float64(nil),
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok, conversion fails, value is not changed",
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: []float64(nil),
|
|
expectError: "code=400, message=failed to bind field value to float64, internal=strconv.ParseFloat: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "nok, conversion fails fast, value is not changed",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=0¶m=nope¶m=100",
|
|
expectValue: []float64(nil),
|
|
expectError: "code=400, message=failed to bind field value to float64, internal=strconv.ParseFloat: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=4.3¶m=0",
|
|
expectValue: []float64{4.3, 0},
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []float64(nil),
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
givenBindErrors: []error{errors.New("previous error")},
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []float64(nil),
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok (must), conversion fails, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: []float64(nil),
|
|
expectError: "code=400, message=failed to bind field value to float64, internal=strconv.ParseFloat: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
b.errors = tc.givenBindErrors
|
|
|
|
var dest []float64
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustFloat64s("param", &dest).BindError()
|
|
} else {
|
|
err = b.Float64s("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Float32(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenNoFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue float32
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=4.3¶m=1",
|
|
expectValue: 4.3,
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: 1.123,
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenNoFailFast: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: 1.123,
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok, conversion fails, value is not changed",
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: 1.123,
|
|
expectError: "code=400, message=failed to bind field value to float32, internal=strconv.ParseFloat: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=4.3¶m=100",
|
|
expectValue: 4.3,
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: 1.123,
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenNoFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: 1.123,
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok (must), conversion fails, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: 1.123,
|
|
expectError: "code=400, message=failed to bind field value to float32, internal=strconv.ParseFloat: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenNoFailFast)
|
|
if tc.givenNoFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
dest := float32(1.123)
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustFloat32("param", &dest).BindError()
|
|
} else {
|
|
err = b.Float32("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Float32s(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue []float32
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=4.3¶m=0",
|
|
expectValue: []float32{4.3, 0},
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []float32(nil),
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
givenBindErrors: []error{errors.New("previous error")},
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []float32(nil),
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok, conversion fails, value is not changed",
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: []float32(nil),
|
|
expectError: "code=400, message=failed to bind field value to float32, internal=strconv.ParseFloat: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "nok, conversion fails fast, value is not changed",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=0¶m=nope¶m=100",
|
|
expectValue: []float32(nil),
|
|
expectError: "code=400, message=failed to bind field value to float32, internal=strconv.ParseFloat: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=4.3¶m=0",
|
|
expectValue: []float32{4.3, 0},
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []float32(nil),
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
givenBindErrors: []error{errors.New("previous error")},
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []float32(nil),
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok (must), conversion fails, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: []float32(nil),
|
|
expectError: "code=400, message=failed to bind field value to float32, internal=strconv.ParseFloat: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
b.errors = tc.givenBindErrors
|
|
|
|
var dest []float32
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustFloat32s("param", &dest).BindError()
|
|
} else {
|
|
err = b.Float32s("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Time(t *testing.T) {
|
|
exampleTime, _ := time.Parse(time.RFC3339, "2020-12-23T09:45:31+02:00")
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
whenLayout string
|
|
expectValue time.Time
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=2020-12-23T09:45:31%2B02:00¶m=2000-01-02T09:45:31%2B00:00",
|
|
whenLayout: time.RFC3339,
|
|
expectValue: exampleTime,
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: time.Time{},
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: time.Time{},
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=2020-12-23T09:45:31%2B02:00¶m=2000-01-02T09:45:31%2B00:00",
|
|
whenLayout: time.RFC3339,
|
|
expectValue: exampleTime,
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: time.Time{},
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: time.Time{},
|
|
expectError: "previous error",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
dest := time.Time{}
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustTime("param", &dest, tc.whenLayout).BindError()
|
|
} else {
|
|
err = b.Time("param", &dest, tc.whenLayout).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Times(t *testing.T) {
|
|
exampleTime, _ := time.Parse(time.RFC3339, "2020-12-23T09:45:31+02:00")
|
|
exampleTime2, _ := time.Parse(time.RFC3339, "2000-01-02T09:45:31+00:00")
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
whenLayout string
|
|
expectValue []time.Time
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=2020-12-23T09:45:31%2B02:00¶m=2000-01-02T09:45:31%2B00:00",
|
|
whenLayout: time.RFC3339,
|
|
expectValue: []time.Time{exampleTime, exampleTime2},
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []time.Time(nil),
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
givenBindErrors: []error{errors.New("previous error")},
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []time.Time(nil),
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=2020-12-23T09:45:31%2B02:00¶m=2000-01-02T09:45:31%2B00:00",
|
|
whenLayout: time.RFC3339,
|
|
expectValue: []time.Time{exampleTime, exampleTime2},
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []time.Time(nil),
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
givenBindErrors: []error{errors.New("previous error")},
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []time.Time(nil),
|
|
expectError: "previous error",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
b.errors = tc.givenBindErrors
|
|
|
|
layout := time.RFC3339
|
|
if tc.whenLayout != "" {
|
|
layout = tc.whenLayout
|
|
}
|
|
|
|
var dest []time.Time
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustTimes("param", &dest, layout).BindError()
|
|
} else {
|
|
err = b.Times("param", &dest, layout).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Duration(t *testing.T) {
|
|
example := 42 * time.Second
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue time.Duration
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=42s¶m=1ms",
|
|
expectValue: example,
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: 0,
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: 0,
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=42s¶m=1ms",
|
|
expectValue: example,
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: 0,
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: 0,
|
|
expectError: "previous error",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
var dest time.Duration
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustDuration("param", &dest).BindError()
|
|
} else {
|
|
err = b.Duration("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_Durations(t *testing.T) {
|
|
exampleDuration := 42 * time.Second
|
|
exampleDuration2 := 1 * time.Millisecond
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue []time.Duration
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=42s¶m=1ms",
|
|
expectValue: []time.Duration{exampleDuration, exampleDuration2},
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []time.Duration(nil),
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
givenBindErrors: []error{errors.New("previous error")},
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []time.Duration(nil),
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=42s¶m=1ms",
|
|
expectValue: []time.Duration{exampleDuration, exampleDuration2},
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []time.Duration(nil),
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
givenBindErrors: []error{errors.New("previous error")},
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []time.Duration(nil),
|
|
expectError: "previous error",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
b.errors = tc.givenBindErrors
|
|
|
|
var dest []time.Duration
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustDurations("param", &dest).BindError()
|
|
} else {
|
|
err = b.Durations("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_BindUnmarshaler(t *testing.T) {
|
|
exampleTime, _ := time.Parse(time.RFC3339, "2020-12-23T09:45:31+02:00")
|
|
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue Timestamp
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=2020-12-23T09:45:31%2B02:00¶m=2000-01-02T09:45:31%2B00:00",
|
|
expectValue: Timestamp(exampleTime),
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: Timestamp{},
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: Timestamp{},
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok, conversion fails, value is not changed",
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: Timestamp{},
|
|
expectError: "code=400, message=failed to bind field value to BindUnmarshaler interface, internal=parsing time \"nope\" as \"2006-01-02T15:04:05Z07:00\": cannot parse \"nope\" as \"2006\", field=param",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=2020-12-23T09:45:31%2B02:00¶m=2000-01-02T09:45:31%2B00:00",
|
|
expectValue: Timestamp(exampleTime),
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: Timestamp{},
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: Timestamp{},
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok (must), conversion fails, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: Timestamp{},
|
|
expectError: "code=400, message=failed to bind field value to BindUnmarshaler interface, internal=parsing time \"nope\" as \"2006-01-02T15:04:05Z07:00\": cannot parse \"nope\" as \"2006\", field=param",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
var dest Timestamp
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustBindUnmarshaler("param", &dest).BindError()
|
|
} else {
|
|
err = b.BindUnmarshaler("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_BindWithDelimiter_types(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
whenURL string
|
|
expect interface{}
|
|
}{
|
|
{
|
|
name: "ok, strings",
|
|
expect: []string{"1", "2", "1"},
|
|
},
|
|
{
|
|
name: "ok, int64",
|
|
expect: []int64{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, int32",
|
|
expect: []int32{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, int16",
|
|
expect: []int16{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, int8",
|
|
expect: []int8{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, int",
|
|
expect: []int{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, uint64",
|
|
expect: []uint64{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, uint32",
|
|
expect: []uint32{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, uint16",
|
|
expect: []uint16{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, uint8",
|
|
expect: []uint8{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, uint",
|
|
expect: []uint{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, float64",
|
|
expect: []float64{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, float32",
|
|
expect: []float32{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, bool",
|
|
whenURL: "/search?param=1,false¶m=true",
|
|
expect: []bool{true, false, true},
|
|
},
|
|
{
|
|
name: "ok, Duration",
|
|
whenURL: "/search?param=1s,42s¶m=1ms",
|
|
expect: []time.Duration{1 * time.Second, 42 * time.Second, 1 * time.Millisecond},
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
URL := "/search?param=1,2¶m=1"
|
|
if tc.whenURL != "" {
|
|
URL = tc.whenURL
|
|
}
|
|
c := createTestContext(URL, nil, nil)
|
|
b := QueryParamsBinder(c)
|
|
|
|
switch tc.expect.(type) {
|
|
case []string:
|
|
var dest []string
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
case []int64:
|
|
var dest []int64
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
case []int32:
|
|
var dest []int32
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
case []int16:
|
|
var dest []int16
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
case []int8:
|
|
var dest []int8
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
case []int:
|
|
var dest []int
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
case []uint64:
|
|
var dest []uint64
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
case []uint32:
|
|
var dest []uint32
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
case []uint16:
|
|
var dest []uint16
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
case []uint8:
|
|
var dest []uint8
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
case []uint:
|
|
var dest []uint
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
case []float64:
|
|
var dest []float64
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
case []float32:
|
|
var dest []float32
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
case []bool:
|
|
var dest []bool
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
case []time.Duration:
|
|
var dest []time.Duration
|
|
assert.NoError(t, b.BindWithDelimiter("param", &dest, ",").BindError())
|
|
assert.Equal(t, tc.expect, dest)
|
|
default:
|
|
assert.Fail(t, "invalid type")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_BindWithDelimiter(t *testing.T) {
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue []int64
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value",
|
|
whenURL: "/search?param=1,2¶m=1",
|
|
expectValue: []int64{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []int64(nil),
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []int64(nil),
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok, conversion fails, value is not changed",
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: []int64(nil),
|
|
expectError: "code=400, message=failed to bind field value to int64, internal=strconv.ParseInt: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=1,2¶m=1",
|
|
expectValue: []int64{1, 2, 1},
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: []int64(nil),
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: []int64(nil),
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok (must), conversion fails, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: []int64(nil),
|
|
expectError: "code=400, message=failed to bind field value to int64, internal=strconv.ParseInt: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
var dest []int64
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustBindWithDelimiter("param", &dest, ",").BindError()
|
|
} else {
|
|
err = b.BindWithDelimiter("param", &dest, ",").BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue, dest)
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBindWithDelimiter_invalidType(t *testing.T) {
|
|
c := createTestContext("/search?param=1¶m=100", nil, nil)
|
|
b := QueryParamsBinder(c)
|
|
|
|
var dest []BindUnmarshaler
|
|
err := b.BindWithDelimiter("param", &dest, ",").BindError()
|
|
assert.Equal(t, []BindUnmarshaler(nil), dest)
|
|
assert.EqualError(t, err, "code=400, message=unsupported bind type, field=param")
|
|
}
|
|
|
|
func TestValueBinder_UnixTime(t *testing.T) {
|
|
exampleTime, _ := time.Parse(time.RFC3339, "2020-12-28T18:36:43+00:00") // => 1609180603
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue time.Time
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value, unix time in seconds",
|
|
whenURL: "/search?param=1609180603¶m=1609180604",
|
|
expectValue: exampleTime,
|
|
},
|
|
{
|
|
name: "ok, binds value, unix time over int32 value",
|
|
whenURL: "/search?param=2147483648¶m=1609180604",
|
|
expectValue: time.Unix(2147483648, 0),
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: time.Time{},
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: time.Time{},
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok, conversion fails, value is not changed",
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: time.Time{},
|
|
expectError: "code=400, message=failed to bind field value to Time, internal=strconv.ParseInt: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=1609180603¶m=1609180604",
|
|
expectValue: exampleTime,
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: time.Time{},
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: time.Time{},
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok (must), conversion fails, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: time.Time{},
|
|
expectError: "code=400, message=failed to bind field value to Time, internal=strconv.ParseInt: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
dest := time.Time{}
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustUnixTime("param", &dest).BindError()
|
|
} else {
|
|
err = b.UnixTime("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue.UnixNano(), dest.UnixNano())
|
|
assert.Equal(t, tc.expectValue.In(time.UTC), dest.In(time.UTC))
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValueBinder_UnixTimeNano(t *testing.T) {
|
|
exampleTime, _ := time.Parse(time.RFC3339, "2020-12-28T18:36:43.000000000+00:00") // => 1609180603
|
|
exampleTimeNano, _ := time.Parse(time.RFC3339Nano, "2020-12-28T18:36:43.123456789+00:00") // => 1609180603123456789
|
|
exampleTimeNanoBelowSec, _ := time.Parse(time.RFC3339Nano, "1970-01-01T00:00:00.999999999+00:00")
|
|
var testCases = []struct {
|
|
name string
|
|
givenFailFast bool
|
|
givenBindErrors []error
|
|
whenURL string
|
|
whenMust bool
|
|
expectValue time.Time
|
|
expectError string
|
|
}{
|
|
{
|
|
name: "ok, binds value, unix time in nano seconds (sec precision)",
|
|
whenURL: "/search?param=1609180603000000000¶m=1609180604",
|
|
expectValue: exampleTime,
|
|
},
|
|
{
|
|
name: "ok, binds value, unix time in nano seconds",
|
|
whenURL: "/search?param=1609180603123456789¶m=1609180604",
|
|
expectValue: exampleTimeNano,
|
|
},
|
|
{
|
|
name: "ok, binds value, unix time in nano seconds (below 1 sec)",
|
|
whenURL: "/search?param=999999999¶m=1609180604",
|
|
expectValue: exampleTimeNanoBelowSec,
|
|
},
|
|
{
|
|
name: "ok, params values empty, value is not changed",
|
|
whenURL: "/search?nope=1",
|
|
expectValue: time.Time{},
|
|
},
|
|
{
|
|
name: "nok, previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: time.Time{},
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok, conversion fails, value is not changed",
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: time.Time{},
|
|
expectError: "code=400, message=failed to bind field value to Time, internal=strconv.ParseInt: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
{
|
|
name: "ok (must), binds value",
|
|
whenMust: true,
|
|
whenURL: "/search?param=1609180603000000000¶m=1609180604",
|
|
expectValue: exampleTime,
|
|
},
|
|
{
|
|
name: "ok (must), params values empty, returns error, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?nope=1",
|
|
expectValue: time.Time{},
|
|
expectError: "code=400, message=required field value is empty, field=param",
|
|
},
|
|
{
|
|
name: "nok (must), previous errors fail fast without binding value",
|
|
givenFailFast: true,
|
|
whenMust: true,
|
|
whenURL: "/search?param=1¶m=100",
|
|
expectValue: time.Time{},
|
|
expectError: "previous error",
|
|
},
|
|
{
|
|
name: "nok (must), conversion fails, value is not changed",
|
|
whenMust: true,
|
|
whenURL: "/search?param=nope¶m=100",
|
|
expectValue: time.Time{},
|
|
expectError: "code=400, message=failed to bind field value to Time, internal=strconv.ParseInt: parsing \"nope\": invalid syntax, field=param",
|
|
},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
c := createTestContext(tc.whenURL, nil, nil)
|
|
b := QueryParamsBinder(c).FailFast(tc.givenFailFast)
|
|
if tc.givenFailFast {
|
|
b.errors = []error{errors.New("previous error")}
|
|
}
|
|
|
|
dest := time.Time{}
|
|
var err error
|
|
if tc.whenMust {
|
|
err = b.MustUnixTimeNano("param", &dest).BindError()
|
|
} else {
|
|
err = b.UnixTimeNano("param", &dest).BindError()
|
|
}
|
|
|
|
assert.Equal(t, tc.expectValue.UnixNano(), dest.UnixNano())
|
|
assert.Equal(t, tc.expectValue.In(time.UTC), dest.In(time.UTC))
|
|
if tc.expectError != "" {
|
|
assert.EqualError(t, err, tc.expectError)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func BenchmarkDefaultBinder_BindInt64_single(b *testing.B) {
|
|
type Opts struct {
|
|
Param int64 `query:"param"`
|
|
}
|
|
c := createTestContext("/search?param=1¶m=100", nil, nil)
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
binder := new(DefaultBinder)
|
|
for i := 0; i < b.N; i++ {
|
|
var dest Opts
|
|
_ = binder.Bind(&dest, c)
|
|
}
|
|
}
|
|
|
|
func BenchmarkValueBinder_BindInt64_single(b *testing.B) {
|
|
c := createTestContext("/search?param=1¶m=100", nil, nil)
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
type Opts struct {
|
|
Param int64
|
|
}
|
|
binder := QueryParamsBinder(c)
|
|
for i := 0; i < b.N; i++ {
|
|
var dest Opts
|
|
_ = binder.Int64("param", &dest.Param).BindError()
|
|
}
|
|
}
|
|
|
|
func BenchmarkRawFunc_Int64_single(b *testing.B) {
|
|
c := createTestContext("/search?param=1¶m=100", nil, nil)
|
|
|
|
rawFunc := func(input string, defaultValue int64) (int64, bool) {
|
|
if input == "" {
|
|
return defaultValue, true
|
|
}
|
|
n, err := strconv.Atoi(input)
|
|
if err != nil {
|
|
return 0, false
|
|
}
|
|
return int64(n), true
|
|
}
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
type Opts struct {
|
|
Param int64
|
|
}
|
|
for i := 0; i < b.N; i++ {
|
|
var dest Opts
|
|
if n, ok := rawFunc(c.QueryParam("param"), 1); ok {
|
|
dest.Param = n
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkDefaultBinder_BindInt64_10_fields(b *testing.B) {
|
|
type Opts struct {
|
|
Int64 int64 `query:"int64"`
|
|
Int32 int32 `query:"int32"`
|
|
Int16 int16 `query:"int16"`
|
|
Int8 int8 `query:"int8"`
|
|
String string `query:"string"`
|
|
|
|
Uint64 uint64 `query:"uint64"`
|
|
Uint32 uint32 `query:"uint32"`
|
|
Uint16 uint16 `query:"uint16"`
|
|
Uint8 uint8 `query:"uint8"`
|
|
Strings []string `query:"strings"`
|
|
}
|
|
c := createTestContext("/search?int64=1&int32=2&int16=3&int8=4&string=test&uint64=5&uint32=6&uint16=7&uint8=8&strings=first&strings=second", nil, nil)
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
binder := new(DefaultBinder)
|
|
for i := 0; i < b.N; i++ {
|
|
var dest Opts
|
|
_ = binder.Bind(&dest, c)
|
|
if dest.Int64 != 1 {
|
|
b.Fatalf("int64!=1")
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkValueBinder_BindInt64_10_fields(b *testing.B) {
|
|
type Opts struct {
|
|
Int64 int64 `query:"int64"`
|
|
Int32 int32 `query:"int32"`
|
|
Int16 int16 `query:"int16"`
|
|
Int8 int8 `query:"int8"`
|
|
String string `query:"string"`
|
|
|
|
Uint64 uint64 `query:"uint64"`
|
|
Uint32 uint32 `query:"uint32"`
|
|
Uint16 uint16 `query:"uint16"`
|
|
Uint8 uint8 `query:"uint8"`
|
|
Strings []string `query:"strings"`
|
|
}
|
|
c := createTestContext("/search?int64=1&int32=2&int16=3&int8=4&string=test&uint64=5&uint32=6&uint16=7&uint8=8&strings=first&strings=second", nil, nil)
|
|
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
binder := QueryParamsBinder(c)
|
|
for i := 0; i < b.N; i++ {
|
|
var dest Opts
|
|
_ = binder.
|
|
Int64("int64", &dest.Int64).
|
|
Int32("int32", &dest.Int32).
|
|
Int16("int16", &dest.Int16).
|
|
Int8("int8", &dest.Int8).
|
|
String("string", &dest.String).
|
|
Uint64("int64", &dest.Uint64).
|
|
Uint32("int32", &dest.Uint32).
|
|
Uint16("int16", &dest.Uint16).
|
|
Uint8("int8", &dest.Uint8).
|
|
Strings("strings", &dest.Strings).
|
|
BindError()
|
|
if dest.Int64 != 1 {
|
|
b.Fatalf("int64!=1")
|
|
}
|
|
}
|
|
}
|