validation_test.go 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. package valid
  2. import (
  3. "context"
  4. "errors"
  5. "strings"
  6. "testing"
  7. "github.com/stretchr/testify/assert"
  8. )
  9. func TestValidate(t *testing.T) {
  10. slice := []String123{String123("abc"), String123("123"), String123("xyz")}
  11. ctxSlice := []Model4{{A: "abc"}, {A: "def"}}
  12. mp := map[string]String123{"c": String123("abc"), "b": String123("123"), "a": String123("xyz")}
  13. mpCtx := map[string]StringValidateContext{"c": StringValidateContext("abc"), "b": StringValidateContext("123"), "a": StringValidateContext("xyz")}
  14. var (
  15. ptr *string
  16. noCtx StringValidate = "abc"
  17. withCtx StringValidateContext = "xyz"
  18. )
  19. tests := []struct {
  20. tag string
  21. value interface{}
  22. err string
  23. errWithContext string
  24. }{
  25. {"t1", 123, "", ""},
  26. {"t2", String123("123"), "", ""},
  27. {"t3", String123("abc"), "error 123", "error 123"},
  28. {"t4", []String123{}, "", ""},
  29. {"t4.1", []StringValidateContext{}, "", ""},
  30. {"t4.2", map[string]StringValidateContext{}, "", ""},
  31. {"t5", slice, "0: error 123; 2: error 123.", "0: error 123; 2: error 123."},
  32. {"t6", &slice, "0: error 123; 2: error 123.", "0: error 123; 2: error 123."},
  33. {"t7", ctxSlice, "", "1: (A: error abc.)."},
  34. {"t8", mp, "a: error 123; c: error 123.", "a: error 123; c: error 123."},
  35. {"t8.1", mpCtx, "a: must be abc; b: must be abc.", "a: must be abc with context; b: must be abc with context."},
  36. {"t9", &mp, "a: error 123; c: error 123.", "a: error 123; c: error 123."},
  37. {"t10", map[string]String123{}, "", ""},
  38. {"t11", ptr, "", ""},
  39. {"t12", noCtx, "called validate", "called validate"},
  40. {"t13", withCtx, "must be abc", "must be abc with context"},
  41. }
  42. for _, test := range tests {
  43. err := Validate(test.value)
  44. assertError(t, test.err, err, test.tag)
  45. // rules that are not context-aware should still be applied in context-aware validation
  46. err = ValidateWithContext(context.Background(), test.value)
  47. assertError(t, test.errWithContext, err, test.tag)
  48. }
  49. // with rules
  50. err := Validate("123", &validateAbc{}, &validateXyz{})
  51. assert.EqualError(t, err, "error abc")
  52. err = Validate("abc", &validateAbc{}, &validateXyz{})
  53. assert.EqualError(t, err, "error xyz")
  54. err = Validate("abcxyz", &validateAbc{}, &validateXyz{})
  55. assert.NoError(t, err)
  56. err = Validate("123", &validateAbc{}, Skip, &validateXyz{})
  57. assert.EqualError(t, err, "error abc")
  58. err = Validate("abc", &validateAbc{}, Skip, &validateXyz{})
  59. assert.NoError(t, err)
  60. err = Validate("123", &validateAbc{}, Skip.When(true), &validateXyz{})
  61. assert.EqualError(t, err, "error abc")
  62. err = Validate("abc", &validateAbc{}, Skip.When(true), &validateXyz{})
  63. assert.NoError(t, err)
  64. err = Validate("123", &validateAbc{}, Skip.When(false), &validateXyz{})
  65. assert.EqualError(t, err, "error abc")
  66. err = Validate("abc", &validateAbc{}, Skip.When(false), &validateXyz{})
  67. assert.EqualError(t, err, "error xyz")
  68. }
  69. func stringEqual(str string) RuleFunc {
  70. return func(value interface{}) error {
  71. s, _ := value.(string)
  72. if s != str {
  73. return errors.New("unexpected string")
  74. }
  75. return nil
  76. }
  77. }
  78. func TestBy(t *testing.T) {
  79. abcRule := By(func(value interface{}) error {
  80. s, _ := value.(string)
  81. if s != "abc" {
  82. return errors.New("must be abc")
  83. }
  84. return nil
  85. })
  86. assert.Nil(t, Validate("abc", abcRule))
  87. err := Validate("xyz", abcRule)
  88. if assert.NotNil(t, err) {
  89. assert.Equal(t, "must be abc", err.Error())
  90. }
  91. xyzRule := By(stringEqual("xyz"))
  92. assert.Nil(t, Validate("xyz", xyzRule))
  93. assert.NotNil(t, Validate("abc", xyzRule))
  94. assert.Nil(t, ValidateWithContext(context.Background(), "xyz", xyzRule))
  95. assert.NotNil(t, ValidateWithContext(context.Background(), "abc", xyzRule))
  96. }
  97. type key int
  98. func TestByWithContext(t *testing.T) {
  99. k := key(1)
  100. abcRule := WithContext(func(ctx context.Context, value interface{}) error {
  101. if ctx.Value(k) != value.(string) {
  102. return errors.New("must be abc")
  103. }
  104. return nil
  105. })
  106. ctx := context.WithValue(context.Background(), k, "abc")
  107. assert.Nil(t, ValidateWithContext(ctx, "abc", abcRule))
  108. err := ValidateWithContext(ctx, "xyz", abcRule)
  109. if assert.NotNil(t, err) {
  110. assert.Equal(t, "must be abc", err.Error())
  111. }
  112. assert.NotNil(t, Validate("abc", abcRule))
  113. }
  114. func Test_skipRule_Validate(t *testing.T) {
  115. assert.Nil(t, Skip.Validate(100))
  116. }
  117. func assertError(t *testing.T, expected string, err error, tag string) {
  118. if expected == "" {
  119. assert.NoError(t, err, tag)
  120. } else {
  121. assert.EqualError(t, err, expected, tag)
  122. }
  123. }
  124. type validateAbc struct{}
  125. func (v *validateAbc) Validate(obj interface{}) error {
  126. if !strings.Contains(obj.(string), "abc") {
  127. return errors.New("error abc")
  128. }
  129. return nil
  130. }
  131. type validateContextAbc struct{}
  132. func (v *validateContextAbc) Validate(obj interface{}) error {
  133. return v.ValidateWithContext(context.Background(), obj)
  134. }
  135. func (v *validateContextAbc) ValidateWithContext(_ context.Context, obj interface{}) error {
  136. if !strings.Contains(obj.(string), "abc") {
  137. return errors.New("error abc")
  138. }
  139. return nil
  140. }
  141. type validateXyz struct{}
  142. func (v *validateXyz) Validate(obj interface{}) error {
  143. if !strings.Contains(obj.(string), "xyz") {
  144. return errors.New("error xyz")
  145. }
  146. return nil
  147. }
  148. type validateContextXyz struct{}
  149. func (v *validateContextXyz) Validate(obj interface{}) error {
  150. return v.ValidateWithContext(context.Background(), obj)
  151. }
  152. func (v *validateContextXyz) ValidateWithContext(_ context.Context, obj interface{}) error {
  153. if !strings.Contains(obj.(string), "xyz") {
  154. return errors.New("error xyz")
  155. }
  156. return nil
  157. }
  158. type validateInternalError struct{}
  159. func (v *validateInternalError) Validate(obj interface{}) error {
  160. if strings.Contains(obj.(string), "internal") {
  161. return NewInternalError(errors.New("error internal"))
  162. }
  163. return nil
  164. }
  165. type Model1 struct {
  166. A string
  167. B string
  168. c string
  169. D *string
  170. E String123
  171. F *String123
  172. G string `json:"g"`
  173. H []string
  174. I map[string]string
  175. }
  176. type String123 string
  177. func (s String123) Validate() error {
  178. if !strings.Contains(string(s), "123") {
  179. return errors.New("error 123")
  180. }
  181. return nil
  182. }
  183. type Model2 struct {
  184. Model3
  185. M3 Model3
  186. B string
  187. }
  188. type Model3 struct {
  189. A string
  190. }
  191. func (m Model3) Validate() error {
  192. return ValidateStruct(&m,
  193. Field(&m.A, &validateAbc{}),
  194. )
  195. }
  196. type Model4 struct {
  197. A string
  198. }
  199. func (m Model4) ValidateWithContext(ctx context.Context) error {
  200. return ValidateStructWithContext(ctx, &m,
  201. Field(&m.A, &validateContextAbc{}),
  202. )
  203. }
  204. type Model5 struct {
  205. Model4
  206. M4 Model4
  207. B string
  208. }
  209. type StringValidate string
  210. func (s StringValidate) Validate() error {
  211. return errors.New("called validate")
  212. }
  213. type StringValidateContext string
  214. func (s StringValidateContext) Validate() error {
  215. if string(s) != "abc" {
  216. return errors.New("must be abc")
  217. }
  218. return nil
  219. }
  220. func (s StringValidateContext) ValidateWithContext(context.Context) error {
  221. if string(s) != "abc" {
  222. return errors.New("must be abc with context")
  223. }
  224. return nil
  225. }