struct_test.go 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. package valid
  2. import (
  3. "context"
  4. "reflect"
  5. "testing"
  6. "github.com/stretchr/testify/assert"
  7. )
  8. type Struct1 struct {
  9. Field1 int
  10. Field2 *int
  11. Field3 []int
  12. Field4 [4]int
  13. field5 int
  14. Struct2
  15. S1 *Struct2
  16. S2 Struct2
  17. JSONField int `json:"some_json_field"`
  18. JSONIgnoredField int `json:"-"`
  19. }
  20. type Struct2 struct {
  21. Field21 string
  22. Field22 string
  23. }
  24. type Struct3 struct {
  25. *Struct2
  26. S1 string
  27. }
  28. func TestFindStructField(t *testing.T) {
  29. var s1 Struct1
  30. v1 := reflect.ValueOf(&s1).Elem()
  31. assert.NotNil(t, findStructField(v1, reflect.ValueOf(&s1.Field1)))
  32. assert.Nil(t, findStructField(v1, reflect.ValueOf(s1.Field2)))
  33. assert.NotNil(t, findStructField(v1, reflect.ValueOf(&s1.Field2)))
  34. assert.Nil(t, findStructField(v1, reflect.ValueOf(s1.Field3)))
  35. assert.NotNil(t, findStructField(v1, reflect.ValueOf(&s1.Field3)))
  36. assert.NotNil(t, findStructField(v1, reflect.ValueOf(&s1.Field4)))
  37. assert.NotNil(t, findStructField(v1, reflect.ValueOf(&s1.field5)))
  38. assert.NotNil(t, findStructField(v1, reflect.ValueOf(&s1.Struct2)))
  39. assert.Nil(t, findStructField(v1, reflect.ValueOf(s1.S1)))
  40. assert.NotNil(t, findStructField(v1, reflect.ValueOf(&s1.S1)))
  41. assert.NotNil(t, findStructField(v1, reflect.ValueOf(&s1.Field21)))
  42. assert.NotNil(t, findStructField(v1, reflect.ValueOf(&s1.Field22)))
  43. assert.NotNil(t, findStructField(v1, reflect.ValueOf(&s1.Struct2.Field22)))
  44. s2 := reflect.ValueOf(&s1.Struct2).Elem()
  45. assert.NotNil(t, findStructField(s2, reflect.ValueOf(&s1.Field21)))
  46. assert.NotNil(t, findStructField(s2, reflect.ValueOf(&s1.Struct2.Field21)))
  47. assert.NotNil(t, findStructField(s2, reflect.ValueOf(&s1.Struct2.Field22)))
  48. s3 := Struct3{
  49. Struct2: &Struct2{},
  50. }
  51. v3 := reflect.ValueOf(&s3).Elem()
  52. assert.NotNil(t, findStructField(v3, reflect.ValueOf(&s3.Struct2)))
  53. assert.NotNil(t, findStructField(v3, reflect.ValueOf(&s3.Field21)))
  54. }
  55. func TestValidateStruct(t *testing.T) {
  56. var m0 *Model1
  57. m1 := Model1{A: "abc", B: "xyz", c: "abc", G: "xyz", H: []string{"abc", "abc"}, I: map[string]string{"foo": "abc"}}
  58. m2 := Model1{E: String123("xyz")}
  59. m3 := Model2{}
  60. m4 := Model2{M3: Model3{A: "abc"}, Model3: Model3{A: "abc"}}
  61. m5 := Model2{Model3: Model3{A: "internal"}}
  62. tests := []struct {
  63. tag string
  64. model interface{}
  65. rules []*FieldRules
  66. err string
  67. }{
  68. // empty rules
  69. {"t1.1", &m1, []*FieldRules{}, ""},
  70. {"t1.2", &m1, []*FieldRules{Field(&m1.A), Field(&m1.B)}, ""},
  71. // normal rules
  72. {"t2.1", &m1, []*FieldRules{Field(&m1.A, &validateAbc{}), Field(&m1.B, &validateXyz{})}, ""},
  73. {"t2.2", &m1, []*FieldRules{Field(&m1.A, &validateXyz{}), Field(&m1.B, &validateAbc{})}, "A: error xyz; B: error abc."},
  74. {"t2.3", &m1, []*FieldRules{Field(&m1.A, &validateXyz{}), Field(&m1.c, &validateXyz{})}, "A: error xyz; c: error xyz."},
  75. {"t2.4", &m1, []*FieldRules{Field(&m1.D, Length(0, 5))}, ""},
  76. {"t2.5", &m1, []*FieldRules{Field(&m1.F, Length(0, 5))}, ""},
  77. {"t2.6", &m1, []*FieldRules{Field(&m1.H, Each(&validateAbc{})), Field(&m1.I, Each(&validateAbc{}))}, ""},
  78. {"t2.7", &m1, []*FieldRules{Field(&m1.H, Each(&validateXyz{})), Field(&m1.I, Each(&validateXyz{}))}, "H: (0: error xyz; 1: error xyz.); I: (foo: error xyz.)."},
  79. // non-struct pointer
  80. {"t3.1", m1, []*FieldRules{}, ErrStructPointer.Error()},
  81. {"t3.2", nil, []*FieldRules{}, ErrStructPointer.Error()},
  82. {"t3.3", m0, []*FieldRules{}, ""},
  83. {"t3.4", &m0, []*FieldRules{}, ErrStructPointer.Error()},
  84. // invalid field spec
  85. {"t4.1", &m1, []*FieldRules{Field(m1)}, ErrFieldPointer(0).Error()},
  86. {"t4.2", &m1, []*FieldRules{Field(&m1)}, ErrFieldNotFound(0).Error()},
  87. // struct tag
  88. {"t5.1", &m1, []*FieldRules{Field(&m1.G, &validateAbc{})}, "g: error abc."},
  89. // validatable field
  90. {"t6.1", &m2, []*FieldRules{Field(&m2.E)}, "E: error 123."},
  91. {"t6.2", &m2, []*FieldRules{Field(&m2.E, Skip)}, ""},
  92. {"t6.3", &m2, []*FieldRules{Field(&m2.E, Skip.When(true))}, ""},
  93. {"t6.4", &m2, []*FieldRules{Field(&m2.E, Skip.When(false))}, "E: error 123."},
  94. // Required, NotNil
  95. {"t7.1", &m2, []*FieldRules{Field(&m2.F, Required)}, "F: cannot be blank."},
  96. {"t7.2", &m2, []*FieldRules{Field(&m2.F, NotNil)}, "F: is required."},
  97. {"t7.3", &m2, []*FieldRules{Field(&m2.F, Skip, Required)}, ""},
  98. {"t7.4", &m2, []*FieldRules{Field(&m2.F, Skip, NotNil)}, ""},
  99. {"t7.5", &m2, []*FieldRules{Field(&m2.F, Skip.When(true), Required)}, ""},
  100. {"t7.6", &m2, []*FieldRules{Field(&m2.F, Skip.When(true), NotNil)}, ""},
  101. {"t7.7", &m2, []*FieldRules{Field(&m2.F, Skip.When(false), Required)}, "F: cannot be blank."},
  102. {"t7.8", &m2, []*FieldRules{Field(&m2.F, Skip.When(false), NotNil)}, "F: is required."},
  103. // embedded structs
  104. {"t8.1", &m3, []*FieldRules{Field(&m3.M3, Skip)}, ""},
  105. {"t8.2", &m3, []*FieldRules{Field(&m3.M3)}, "M3: (A: error abc.)."},
  106. {"t8.3", &m3, []*FieldRules{Field(&m3.Model3, Skip)}, ""},
  107. {"t8.4", &m3, []*FieldRules{Field(&m3.Model3)}, "A: error abc."},
  108. {"t8.5", &m4, []*FieldRules{Field(&m4.M3)}, ""},
  109. {"t8.6", &m4, []*FieldRules{Field(&m4.Model3)}, ""},
  110. {"t8.7", &m3, []*FieldRules{Field(&m3.A, Required), Field(&m3.B, Required)}, "A: cannot be blank; B: cannot be blank."},
  111. {"t8.8", &m3, []*FieldRules{Field(&m4.A, Required)}, "field #0 cannot be found in the struct"},
  112. // internal error
  113. {"t9.1", &m5, []*FieldRules{Field(&m5.A, &validateAbc{}), Field(&m5.B, Required), Field(&m5.A, &validateInternalError{})}, "error internal"},
  114. }
  115. for _, test := range tests {
  116. err1 := ValidateStruct(test.model, test.rules...)
  117. err2 := ValidateStructWithContext(context.Background(), test.model, test.rules...)
  118. assertError(t, test.err, err1, test.tag)
  119. assertError(t, test.err, err2, test.tag)
  120. }
  121. // embedded struct
  122. err := Validate(&m3)
  123. assert.EqualError(t, err, "A: error abc.")
  124. a := struct {
  125. Name string
  126. Value string
  127. }{"name", "demo"}
  128. err = ValidateStruct(&a,
  129. Field(&a.Name, Required),
  130. Field(&a.Value, Required, Length(5, 10)),
  131. )
  132. assert.EqualError(t, err, "Value: the length must be between 5 and 10.")
  133. }
  134. func TestValidateStructWithContext(t *testing.T) {
  135. m1 := Model1{A: "abc", B: "xyz", c: "abc", G: "xyz"}
  136. m2 := Model2{Model3: Model3{A: "internal"}}
  137. m3 := Model5{}
  138. tests := []struct {
  139. tag string
  140. model interface{}
  141. rules []*FieldRules
  142. err string
  143. }{
  144. // normal rules
  145. {"t1.1", &m1, []*FieldRules{Field(&m1.A, &validateContextAbc{}), Field(&m1.B, &validateContextXyz{})}, ""},
  146. {"t1.2", &m1, []*FieldRules{Field(&m1.A, &validateContextXyz{}), Field(&m1.B, &validateContextAbc{})}, "A: error xyz; B: error abc."},
  147. {"t1.3", &m1, []*FieldRules{Field(&m1.A, &validateContextXyz{}), Field(&m1.c, &validateContextXyz{})}, "A: error xyz; c: error xyz."},
  148. {"t1.4", &m1, []*FieldRules{Field(&m1.G, &validateContextAbc{})}, "g: error abc."},
  149. // skip rule
  150. {"t2.1", &m1, []*FieldRules{Field(&m1.G, Skip, &validateContextAbc{})}, ""},
  151. {"t2.2", &m1, []*FieldRules{Field(&m1.G, &validateContextAbc{}, Skip)}, "g: error abc."},
  152. // internal error
  153. {"t3.1", &m2, []*FieldRules{Field(&m2.A, &validateContextAbc{}), Field(&m2.B, Required), Field(&m2.A, &validateInternalError{})}, "error internal"},
  154. }
  155. for _, test := range tests {
  156. err := ValidateStructWithContext(context.Background(), test.model, test.rules...)
  157. assertError(t, test.err, err, test.tag)
  158. }
  159. //embedded struct
  160. err := ValidateWithContext(context.Background(), &m3)
  161. if assert.NotNil(t, err) {
  162. assert.Equal(t, "A: error abc.", err.Error())
  163. }
  164. a := struct {
  165. Name string
  166. Value string
  167. }{"name", "demo"}
  168. err = ValidateStructWithContext(context.Background(), &a,
  169. Field(&a.Name, Required),
  170. Field(&a.Value, Required, Length(5, 10)),
  171. )
  172. if assert.NotNil(t, err) {
  173. assert.Equal(t, "Value: the length must be between 5 and 10.", err.Error())
  174. }
  175. }
  176. func Test_getErrorFieldName(t *testing.T) {
  177. var s1 Struct1
  178. v1 := reflect.ValueOf(&s1).Elem()
  179. sf1 := findStructField(v1, reflect.ValueOf(&s1.Field1))
  180. assert.NotNil(t, sf1)
  181. assert.Equal(t, "Field1", getErrorFieldName(sf1))
  182. jsonField := findStructField(v1, reflect.ValueOf(&s1.JSONField))
  183. assert.NotNil(t, jsonField)
  184. assert.Equal(t, "some_json_field", getErrorFieldName(jsonField))
  185. jsonIgnoredField := findStructField(v1, reflect.ValueOf(&s1.JSONIgnoredField))
  186. assert.NotNil(t, jsonIgnoredField)
  187. assert.Equal(t, "JSONIgnoredField", getErrorFieldName(jsonIgnoredField))
  188. }