validator_test.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. package requestutils_test
  2. import (
  3. "fmt"
  4. "strings"
  5. "testing"
  6. "github.com/stretchr/testify/assert"
  7. "github.com/porter-dev/porter/api/server/shared/apierrors"
  8. "github.com/porter-dev/porter/api/server/shared/requestutils"
  9. )
  10. const (
  11. requiredErrorFmt = "validation failed on field '%s' on condition 'required'"
  12. simpleConditionErrorFmt = "validation failed on field '%s' on condition '%s'"
  13. paramErrorFmt = "validation failed on field '%s' on condition '%s' [ %s ]: got %s"
  14. )
  15. type validationErrObjectTest struct {
  16. valErrObj *requestutils.ValidationErrObject
  17. expStr string
  18. }
  19. var validationErrObjectTests = []validationErrObjectTest{
  20. {
  21. valErrObj: &requestutils.ValidationErrObject{
  22. Field: "username",
  23. Condition: "required",
  24. Param: "",
  25. ActualValue: nil,
  26. },
  27. expStr: fmt.Sprintf(requiredErrorFmt, "username"),
  28. },
  29. {
  30. valErrObj: &requestutils.ValidationErrObject{
  31. Field: "storage",
  32. Condition: "oneof",
  33. Param: "secret configmap",
  34. ActualValue: "notsecret",
  35. },
  36. expStr: fmt.Sprintf(paramErrorFmt, "storage", "oneof", "secret configmap", "'notsecret'"),
  37. },
  38. {
  39. valErrObj: &requestutils.ValidationErrObject{
  40. Field: "storage",
  41. Condition: "oneof",
  42. Param: "secret configmap",
  43. ActualValue: 1,
  44. },
  45. expStr: fmt.Sprintf(paramErrorFmt, "storage", "oneof", "secret configmap", "1"),
  46. },
  47. {
  48. valErrObj: &requestutils.ValidationErrObject{
  49. Field: "storage",
  50. Condition: "oneof",
  51. Param: "secret configmap",
  52. ActualValue: []string{"secret1", "secret2"},
  53. },
  54. expStr: fmt.Sprintf(paramErrorFmt, "storage", "oneof", "secret configmap", "[ secret1 secret2 ]"),
  55. },
  56. {
  57. valErrObj: &requestutils.ValidationErrObject{
  58. Field: "storage",
  59. Condition: "oneof",
  60. Param: "secret configmap",
  61. ActualValue: []int{1, 2},
  62. },
  63. expStr: fmt.Sprintf(paramErrorFmt, "storage", "oneof", "secret configmap", "[ 1 2 ]"),
  64. },
  65. {
  66. valErrObj: &requestutils.ValidationErrObject{
  67. Field: "storage",
  68. Condition: "oneof",
  69. Param: "secret configmap",
  70. // for nil values, we convert the actual value to null
  71. ActualValue: nil,
  72. },
  73. expStr: fmt.Sprintf(paramErrorFmt, "storage", "oneof", "secret configmap", "null"),
  74. },
  75. {
  76. valErrObj: &requestutils.ValidationErrObject{
  77. Field: "storage",
  78. Condition: "oneof",
  79. Param: "secret configmap",
  80. // for unrecognized types, we don't cast to value
  81. ActualValue: map[string]string{
  82. "not": "cast",
  83. },
  84. },
  85. expStr: fmt.Sprintf(paramErrorFmt, "storage", "oneof", "secret configmap", "invalid type"),
  86. },
  87. }
  88. func TestValidationErrObject(t *testing.T) {
  89. assert := assert.New(t)
  90. for _, test := range validationErrObjectTests {
  91. // test that the function outputs the expected readable error message
  92. readableStr := test.valErrObj.SafeExternalError()
  93. expReadableStr := test.expStr
  94. assert.Equal(
  95. expReadableStr,
  96. readableStr,
  97. "readable string not equal: expected %s, got %s",
  98. expReadableStr,
  99. readableStr,
  100. )
  101. }
  102. }
  103. type validationTest struct {
  104. description string
  105. valObj interface{}
  106. expErr bool
  107. expErrStrings []string
  108. }
  109. type validationTestObj struct {
  110. ID uint `form:"required"`
  111. Name string `form:"required"`
  112. Email string `form:"email"`
  113. Storage string `form:"oneof=sqlite postgres"`
  114. }
  115. var validationTests = []validationTest{
  116. {
  117. description: "Missing all fields",
  118. valObj: &validationTestObj{},
  119. expErr: true,
  120. expErrStrings: []string{
  121. fmt.Sprintf(requiredErrorFmt, "ID"),
  122. fmt.Sprintf(requiredErrorFmt, "Name"),
  123. fmt.Sprintf(simpleConditionErrorFmt, "Email", "email"),
  124. fmt.Sprintf(paramErrorFmt, "Storage", "oneof", "sqlite postgres", "''"),
  125. },
  126. },
  127. {
  128. description: "Fails email validation",
  129. valObj: &validationTestObj{
  130. ID: 1,
  131. Name: "whatever",
  132. Email: "notanemail",
  133. Storage: "postgres",
  134. },
  135. expErr: true,
  136. expErrStrings: []string{
  137. fmt.Sprintf(simpleConditionErrorFmt, "Email", "email"),
  138. },
  139. },
  140. {
  141. description: "Should pass all",
  142. valObj: &validationTestObj{
  143. ID: 1,
  144. Name: "whatever",
  145. Email: "anemail@gmail.com",
  146. Storage: "postgres",
  147. },
  148. expErr: false,
  149. expErrStrings: []string{},
  150. },
  151. }
  152. func TestValidation(t *testing.T) {
  153. assert := assert.New(t)
  154. validator := requestutils.NewDefaultValidator()
  155. for _, test := range validationTests {
  156. // test that the function outputs the expected readable error message
  157. err := validator.Validate(test.valObj)
  158. assert.Equal(
  159. err != nil,
  160. test.expErr,
  161. "[ %s ]: expected error was %t, got %t",
  162. test.description,
  163. err != nil,
  164. test.expErr,
  165. )
  166. if err != nil && test.expErr {
  167. readableStrArr := strings.Split(err.Error(), ",")
  168. expReadableStrArr := test.expErrStrings
  169. assert.ElementsMatch(
  170. expReadableStrArr,
  171. readableStrArr,
  172. "[ %s ]: readable string not equal",
  173. test.description,
  174. )
  175. // check that external and internal errors are returned as well
  176. assert.Equal(
  177. 400,
  178. err.GetStatusCode(),
  179. "[ %s ]: status code not equal",
  180. test.description,
  181. )
  182. }
  183. }
  184. }
  185. func TestValidationNilParam(t *testing.T) {
  186. validator := requestutils.NewDefaultValidator()
  187. err := validator.Validate(nil)
  188. expErr := apierrors.NewErrInternal(fmt.Errorf("could not cast err to validator.ValidationErrors"))
  189. // check that error type is of type apierrors.RequestError and that
  190. // message is correct
  191. assert.EqualError(t, err, expErr.Error(), "nil param error not internal server error")
  192. var expErrTarget apierrors.RequestError
  193. assert.ErrorAs(t, err, &expErrTarget)
  194. }
  195. func TestErrFailedRequestValidation(t *testing.T) {
  196. assert := assert.New(t)
  197. // just check that status code is 400 and all errors are set
  198. expErrStr := "readable error"
  199. err := requestutils.NewErrFailedRequestValidation(expErrStr)
  200. assert.Equal(
  201. expErrStr,
  202. err.Error(),
  203. "incorrect value for Error() method",
  204. )
  205. assert.Equal(
  206. expErrStr,
  207. err.ExternalError(),
  208. "incorrect value for ExternalError() method",
  209. )
  210. // check that the status code is 400
  211. assert.Equal(
  212. expErrStr,
  213. err.InternalError(),
  214. "incorrect value for InternalError() method",
  215. )
  216. }