123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266 |
- package valid
- import (
- "context"
- "errors"
- "strings"
- "testing"
- "github.com/stretchr/testify/assert"
- )
- func TestValidate(t *testing.T) {
- slice := []String123{String123("abc"), String123("123"), String123("xyz")}
- ctxSlice := []Model4{{A: "abc"}, {A: "def"}}
- mp := map[string]String123{"c": String123("abc"), "b": String123("123"), "a": String123("xyz")}
- mpCtx := map[string]StringValidateContext{"c": StringValidateContext("abc"), "b": StringValidateContext("123"), "a": StringValidateContext("xyz")}
- var (
- ptr *string
- noCtx StringValidate = "abc"
- withCtx StringValidateContext = "xyz"
- )
- tests := []struct {
- tag string
- value interface{}
- err string
- errWithContext string
- }{
- {"t1", 123, "", ""},
- {"t2", String123("123"), "", ""},
- {"t3", String123("abc"), "error 123", "error 123"},
- {"t4", []String123{}, "", ""},
- {"t4.1", []StringValidateContext{}, "", ""},
- {"t4.2", map[string]StringValidateContext{}, "", ""},
- {"t5", slice, "0: error 123; 2: error 123.", "0: error 123; 2: error 123."},
- {"t6", &slice, "0: error 123; 2: error 123.", "0: error 123; 2: error 123."},
- {"t7", ctxSlice, "", "1: (A: error abc.)."},
- {"t8", mp, "a: error 123; c: error 123.", "a: error 123; c: error 123."},
- {"t8.1", mpCtx, "a: must be abc; b: must be abc.", "a: must be abc with context; b: must be abc with context."},
- {"t9", &mp, "a: error 123; c: error 123.", "a: error 123; c: error 123."},
- {"t10", map[string]String123{}, "", ""},
- {"t11", ptr, "", ""},
- {"t12", noCtx, "called validate", "called validate"},
- {"t13", withCtx, "must be abc", "must be abc with context"},
- }
- for _, test := range tests {
- err := Validate(test.value)
- assertError(t, test.err, err, test.tag)
- // rules that are not context-aware should still be applied in context-aware validation
- err = ValidateWithContext(context.Background(), test.value)
- assertError(t, test.errWithContext, err, test.tag)
- }
- // with rules
- err := Validate("123", &validateAbc{}, &validateXyz{})
- assert.EqualError(t, err, "error abc")
- err = Validate("abc", &validateAbc{}, &validateXyz{})
- assert.EqualError(t, err, "error xyz")
- err = Validate("abcxyz", &validateAbc{}, &validateXyz{})
- assert.NoError(t, err)
- err = Validate("123", &validateAbc{}, Skip, &validateXyz{})
- assert.EqualError(t, err, "error abc")
- err = Validate("abc", &validateAbc{}, Skip, &validateXyz{})
- assert.NoError(t, err)
- err = Validate("123", &validateAbc{}, Skip.When(true), &validateXyz{})
- assert.EqualError(t, err, "error abc")
- err = Validate("abc", &validateAbc{}, Skip.When(true), &validateXyz{})
- assert.NoError(t, err)
- err = Validate("123", &validateAbc{}, Skip.When(false), &validateXyz{})
- assert.EqualError(t, err, "error abc")
- err = Validate("abc", &validateAbc{}, Skip.When(false), &validateXyz{})
- assert.EqualError(t, err, "error xyz")
- }
- func stringEqual(str string) RuleFunc {
- return func(value interface{}) error {
- s, _ := value.(string)
- if s != str {
- return errors.New("unexpected string")
- }
- return nil
- }
- }
- func TestBy(t *testing.T) {
- abcRule := By(func(value interface{}) error {
- s, _ := value.(string)
- if s != "abc" {
- return errors.New("must be abc")
- }
- return nil
- })
- assert.Nil(t, Validate("abc", abcRule))
- err := Validate("xyz", abcRule)
- if assert.NotNil(t, err) {
- assert.Equal(t, "must be abc", err.Error())
- }
- xyzRule := By(stringEqual("xyz"))
- assert.Nil(t, Validate("xyz", xyzRule))
- assert.NotNil(t, Validate("abc", xyzRule))
- assert.Nil(t, ValidateWithContext(context.Background(), "xyz", xyzRule))
- assert.NotNil(t, ValidateWithContext(context.Background(), "abc", xyzRule))
- }
- type key int
- func TestByWithContext(t *testing.T) {
- k := key(1)
- abcRule := WithContext(func(ctx context.Context, value interface{}) error {
- if ctx.Value(k) != value.(string) {
- return errors.New("must be abc")
- }
- return nil
- })
- ctx := context.WithValue(context.Background(), k, "abc")
- assert.Nil(t, ValidateWithContext(ctx, "abc", abcRule))
- err := ValidateWithContext(ctx, "xyz", abcRule)
- if assert.NotNil(t, err) {
- assert.Equal(t, "must be abc", err.Error())
- }
- assert.NotNil(t, Validate("abc", abcRule))
- }
- func Test_skipRule_Validate(t *testing.T) {
- assert.Nil(t, Skip.Validate(100))
- }
- func assertError(t *testing.T, expected string, err error, tag string) {
- if expected == "" {
- assert.NoError(t, err, tag)
- } else {
- assert.EqualError(t, err, expected, tag)
- }
- }
- type validateAbc struct{}
- func (v *validateAbc) Validate(obj interface{}) error {
- if !strings.Contains(obj.(string), "abc") {
- return errors.New("error abc")
- }
- return nil
- }
- type validateContextAbc struct{}
- func (v *validateContextAbc) Validate(obj interface{}) error {
- return v.ValidateWithContext(context.Background(), obj)
- }
- func (v *validateContextAbc) ValidateWithContext(_ context.Context, obj interface{}) error {
- if !strings.Contains(obj.(string), "abc") {
- return errors.New("error abc")
- }
- return nil
- }
- type validateXyz struct{}
- func (v *validateXyz) Validate(obj interface{}) error {
- if !strings.Contains(obj.(string), "xyz") {
- return errors.New("error xyz")
- }
- return nil
- }
- type validateContextXyz struct{}
- func (v *validateContextXyz) Validate(obj interface{}) error {
- return v.ValidateWithContext(context.Background(), obj)
- }
- func (v *validateContextXyz) ValidateWithContext(_ context.Context, obj interface{}) error {
- if !strings.Contains(obj.(string), "xyz") {
- return errors.New("error xyz")
- }
- return nil
- }
- type validateInternalError struct{}
- func (v *validateInternalError) Validate(obj interface{}) error {
- if strings.Contains(obj.(string), "internal") {
- return NewInternalError(errors.New("error internal"))
- }
- return nil
- }
- type Model1 struct {
- A string
- B string
- c string
- D *string
- E String123
- F *String123
- G string `json:"g"`
- H []string
- I map[string]string
- }
- type String123 string
- func (s String123) Validate() error {
- if !strings.Contains(string(s), "123") {
- return errors.New("error 123")
- }
- return nil
- }
- type Model2 struct {
- Model3
- M3 Model3
- B string
- }
- type Model3 struct {
- A string
- }
- func (m Model3) Validate() error {
- return ValidateStruct(&m,
- Field(&m.A, &validateAbc{}),
- )
- }
- type Model4 struct {
- A string
- }
- func (m Model4) ValidateWithContext(ctx context.Context) error {
- return ValidateStructWithContext(ctx, &m,
- Field(&m.A, &validateContextAbc{}),
- )
- }
- type Model5 struct {
- Model4
- M4 Model4
- B string
- }
- type StringValidate string
- func (s StringValidate) Validate() error {
- return errors.New("called validate")
- }
- type StringValidateContext string
- func (s StringValidateContext) Validate() error {
- if string(s) != "abc" {
- return errors.New("must be abc")
- }
- return nil
- }
- func (s StringValidateContext) ValidateWithContext(context.Context) error {
- if string(s) != "abc" {
- return errors.New("must be abc with context")
- }
- return nil
- }
|