Repository: getkin/kin-openapi
Pull request #921 (2024-03-09 00:40:24)

openapi3filter: fix array of primitives query parameter types
Previously always returned as `[]string`


Patch:
diff --git a/openapi3/schema.go b/openapi3/schema.go
index 32027a1..1b767f8 100644
--- a/openapi3/schema.go
+++ b/openapi3/schema.go
@@ -1737,7 +1737,7 @@ func (schema *Schema) visitJSONString(settings *schemaValidationSettings, value
 				}
 			case f.regexp == nil && f.callback != nil:
 				if err := f.callback(value); err != nil {
-					var schemaErr = &SchemaError{}
+					schemaErr := &SchemaError{}
 					if errors.As(err, &schemaErr) {
 						formatStrErr = fmt.Sprintf(`string doesn't match the format %q (%s)`, format, schemaErr.Reason)
 					} else {
diff --git a/openapi3filter/req_resp_decoder.go b/openapi3filter/req_resp_decoder.go
index 60afc54..2861349 100644
--- a/openapi3filter/req_resp_decoder.go
+++ b/openapi3filter/req_resp_decoder.go
@@ -914,6 +914,7 @@ func findNestedSchema(parentSchema *openapi3.SchemaRef, keys []string) (*openapi
 // The function returns an error when an error happened while parse object's properties.
 func makeObject(props map[string]string, schema *openapi3.SchemaRef) (map[string]interface{}, error) {
 	obj := make(map[string]interface{})
+
 	for propName, propSchema := range schema.Value.Properties {
 		switch {
 		case propSchema.Value.Type.Is("array"):
@@ -924,7 +925,12 @@ func makeObject(props map[string]string, schema *openapi3.SchemaRef) (map[string
 					return nil, handlePropParseError([]string{propName}, err)
 				}
 			}
-			obj[propName] = vals
+
+			ivals, err := convertArrayParameterToType(vals, propSchema.Value.Items.Value.Type)
+			if err != nil {
+				return nil, handlePropParseError([]string{propName}, err)
+			}
+			obj[propName] = ivals
 		case propSchema.Value.Type.Is("object"):
 			for prop := range props {
 				if !strings.HasPrefix(prop, propName+urlDecoderDelimiter) {
@@ -943,7 +949,11 @@ func makeObject(props map[string]string, schema *openapi3.SchemaRef) (map[string
 							return nil, handlePropParseError(mapKeys, err)
 						}
 					}
-					deepSet(obj, mapKeys, vals)
+					ivals, err := convertArrayParameterToType(vals, nestedSchema.Value.Items.Value.Type)
+					if err != nil {
+						return nil, handlePropParseError(mapKeys, err)
+					}
+					deepSet(obj, mapKeys, ivals)
 					continue
 				}
 				value, err := parsePrimitive(props[prop], nestedSchema)
@@ -960,9 +970,55 @@ func makeObject(props map[string]string, schema *openapi3.SchemaRef) (map[string
 			obj[propName] = value
 		}
 	}
+
 	return obj, nil
 }
 
+func convertArrayParameterToType(strArray []string, typ *openapi3.Types) (interface{}, error) {
+	var iarr []interface{}
+	switch {
+	case typ.Permits(openapi3.TypeBoolean):
+		for _, str := range strArray {
+			if str == "" {
+				continue
+			}
+			parsedBool, err := strconv.ParseBool(str)
+			if err != nil {
+				return nil, err
+			}
+			iarr = append(iarr, parsedBool)
+		}
+	case typ.Permits(openapi3.TypeInteger):
+		for _, str := range strArray {
+			if str == "" {
+				continue
+			}
+			parsedInt, err := strconv.Atoi(str)
+			if err != nil {
+				return nil, err
+			}
+			iarr = append(iarr, parsedInt)
+		}
+	case typ.Permits(openapi3.TypeNumber):
+		for _, str := range strArray {
+			if str == "" {
+				continue
+			}
+			parsedFloat, err := strconv.ParseFloat(str, 64)
+			if err != nil {
+				return nil, err
+			}
+			iarr = append(iarr, parsedFloat)
+		}
+	case typ.Permits(openapi3.TypeString):
+		return strArray, nil
+	default:
+		return nil, fmt.Errorf("unsupported parameter array type: %s", typ)
+	}
+
+	return iarr, nil
+}
+
 func handlePropParseError(path []string, err error) error {
 	if v, ok := err.(*ParseError); ok {
 		return &ParseError{path: pathFromKeys(path), Cause: v}


Test patch:
diff --git a/openapi3filter/testdata/fixtures/petstore.json b/openapi3filter/testdata/fixtures/petstore.json
index 1a8cd8c..7d3c97a 100644
--- a/openapi3filter/testdata/fixtures/petstore.json
+++ b/openapi3filter/testdata/fixtures/petstore.json
@@ -140,6 +140,98 @@
         }
       }
     },
+    "/pet/filter": {
+      "get": {
+        "tags": [
+          "pet"
+        ],
+        "summary": "Finds Pets by status",
+        "operationId": "filterPets",
+        "parameters": [
+          {
+            "in": "query",
+            "name": "deepFilter",
+            "style": "deepObject",
+            "explode": true,
+            "allowReserved": true,
+            "schema": {
+              "type": "object",
+              "properties": {
+                "strings": {
+                  "type": "array",
+                  "items": {
+                    "type": "string"
+                  }
+                },
+                "booleans": {
+                  "type": "array",
+                  "items": {
+                    "type": "boolean"
+                  }
+                },
+                "numbers": {
+                  "type": "array",
+                  "items": {
+                    "type": "number"
+                  }
+                },
+                "integers": {
+                  "type": "array",
+                  "items": {
+                    "type": "integer"
+                  }
+                }
+              }
+            }
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "successful operation",
+            "content": {
+              "application/xml": {
+                "schema": {
+                  "type": "array",
+                  "items": {
+                    "allOf": [
+                      {
+                        "$ref": "#/components/schemas/Pet"
+                      },
+                      {
+                        "$ref": "#/components/schemas/PetRequiredProperties"
+                      }
+                    ]
+                  }
+                }
+              },
+              "application/json": {
+                "schema": {
+                  "type": "array",
+                  "items": {
+                    "allOf": [
+                      {
+                        "$ref": "#/components/schemas/Pet"
+                      },
+                      {
+                        "$ref": "#/components/schemas/PetRequiredProperties"
+                      }
+                    ]
+                  }
+                }
+              }
+            }
+          }
+        },
+        "security": [
+          {
+            "petstore_auth": [
+              "write:pets",
+              "read:pets"
+            ]
+          }
+        ]
+      }
+    },
     "/pet/findByStatus": {
       "get": {
         "tags": [
@@ -178,8 +270,12 @@
                   "type": "array",
                   "items": {
                     "allOf": [
-                      {"$ref": "#/components/schemas/Pet"},
-                      {"$ref": "#/components/schemas/PetRequiredProperties"}
+                      {
+                        "$ref": "#/components/schemas/Pet"
+                      },
+                      {
+                        "$ref": "#/components/schemas/PetRequiredProperties"
+                      }
                     ]
                   }
                 }
@@ -189,8 +285,12 @@
                   "type": "array",
                   "items": {
                     "allOf": [
-                      {"$ref": "#/components/schemas/Pet"},
-                      {"$ref": "#/components/schemas/PetRequiredProperties"}
+                      {
+                        "$ref": "#/components/schemas/Pet"
+                      },
+                      {
+                        "$ref": "#/components/schemas/PetRequiredProperties"
+                      }
                     ]
                   }
                 }
@@ -283,8 +383,12 @@
                   "type": "array",
                   "items": {
                     "allOf": [
-                      {"$ref": "#/components/schemas/Pet"},
-                      {"$ref": "#/components/schemas/PetRequiredProperties"}
+                      {
+                        "$ref": "#/components/schemas/Pet"
+                      },
+                      {
+                        "$ref": "#/components/schemas/PetRequiredProperties"
+                      }
                     ]
                   }
                 }
@@ -294,8 +398,12 @@
                   "type": "array",
                   "items": {
                     "allOf": [
-                      {"$ref": "#/components/schemas/Pet"},
-                      {"$ref": "#/components/schemas/PetRequiredProperties"}
+                      {
+                        "$ref": "#/components/schemas/Pet"
+                      },
+                      {
+                        "$ref": "#/components/schemas/PetRequiredProperties"
+                      }
                     ]
                   }
                 }
diff --git a/openapi3filter/validation_error_test.go b/openapi3filter/validation_error_test.go
index 70b46c7..6fa7948 100644
--- a/openapi3filter/validation_error_test.go
+++ b/openapi3filter/validation_error_test.go
@@ -108,8 +108,10 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrReason: routers.ErrMethodNotAllowed.Error(),
 			// TODO: By HTTP spec, this should have an Allow header with what is allowed
 			// but kin-openapi doesn't provide us the requested method or path, so impossible to provide details
-			wantErrResponse: &ValidationError{Status: http.StatusMethodNotAllowed,
-				Title: routers.ErrMethodNotAllowed.Error()},
+			wantErrResponse: &ValidationError{
+				Status: http.StatusMethodNotAllowed,
+				Title:  routers.ErrMethodNotAllowed.Error(),
+			},
 		},
 		{
 			name: "error - missing body on POST",
@@ -117,8 +119,10 @@ func getValidationTests(t *testing.T) []*validationTest {
 				r: missingBody1,
 			},
 			wantErrBody: "request body has an error: " + ErrInvalidRequired.Error(),
-			wantErrResponse: &ValidationError{Status: http.StatusBadRequest,
-				Title: "request body has an error: " + ErrInvalidRequired.Error()},
+			wantErrResponse: &ValidationError{
+				Status: http.StatusBadRequest,
+				Title:  "request body has an error: " + ErrInvalidRequired.Error(),
+			},
 		},
 		{
 			name: "error - empty body on POST",
@@ -126,8 +130,10 @@ func getValidationTests(t *testing.T) []*validationTest {
 				r: missingBody2,
 			},
 			wantErrBody: "request body has an error: " + ErrInvalidRequired.Error(),
-			wantErrResponse: &ValidationError{Status: http.StatusBadRequest,
-				Title: "request body has an error: " + ErrInvalidRequired.Error()},
+			wantErrResponse: &ValidationError{
+				Status: http.StatusBadRequest,
+				Title:  "request body has an error: " + ErrInvalidRequired.Error(),
+			},
 		},
 
 		//
@@ -140,8 +146,10 @@ func getValidationTests(t *testing.T) []*validationTest {
 				r: noContentType,
 			},
 			wantErrReason: prefixInvalidCT + ` ""`,
-			wantErrResponse: &ValidationError{Status: http.StatusUnsupportedMediaType,
-				Title: "header Content-Type is required"},
+			wantErrResponse: &ValidationError{
+				Status: http.StatusUnsupportedMediaType,
+				Title:  "header Content-Type is required",
+			},
 		},
 		{
 			name: "error - unknown content-type on POST",
@@ -151,8 +159,10 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrReason:      "failed to decode request body",
 			wantErrParseKind:   KindUnsupportedFormat,
 			wantErrParseReason: prefixUnsupportedCT + ` "application/xml"`,
-			wantErrResponse: &ValidationError{Status: http.StatusUnsupportedMediaType,
-				Title: prefixUnsupportedCT + ` "application/xml"`},
+			wantErrResponse: &ValidationError{
+				Status: http.StatusUnsupportedMediaType,
+				Title:  prefixUnsupportedCT + ` "application/xml"`,
+			},
 		},
 		{
 			name: "error - unsupported content-type on POST",
@@ -160,8 +170,10 @@ func getValidationTests(t *testing.T) []*validationTest {
 				r: unsupportedContentType,
 			},
 			wantErrReason: prefixInvalidCT + ` "text/plain"`,
-			wantErrResponse: &ValidationError{Status: http.StatusUnsupportedMediaType,
-				Title: prefixUnsupportedCT + ` "text/plain"`},
+			wantErrResponse: &ValidationError{
+				Status: http.StatusUnsupportedMediaType,
+				Title:  prefixUnsupportedCT + ` "text/plain"`,
+			},
 		},
 		{
 			name: "success - no content-type header required on GET",
@@ -173,7 +185,57 @@ func getValidationTests(t *testing.T) []*validationTest {
 		//
 		// Query strings
 		//
-
+		{
+			name: "empty deepobject query parameter",
+			args: validationArgs{
+				r: newPetstoreRequest(t, http.MethodGet, "/pet/filter", nil),
+			},
+		},
+		{
+			name: "deepobject query parameter type",
+			args: validationArgs{
+				r: newPetstoreRequest(t, http.MethodGet, "/pet/filter?deepFilter[booleans]=true&deepFilter[integers]=1&deepFilter[strings]=foo%26o&deepFilter[numbers]=1", nil),
+			},
+		},
+		{
+			name: "error - incorrect deepobject query parameter type bool",
+			args: validationArgs{
+				r: newPetstoreRequest(t, http.MethodGet, "/pet/filter?deepFilter[booleans]=notbool", nil),
+			},
+			wantErrParam:   "deepFilter",
+			wantErrBody:    `parameter "deepFilter" in query has an error: path booleans: value notbool: an invalid boolean: invalid syntax`,
+			wantErrParamIn: "query",
+			wantErrResponse: &ValidationError{
+				Status: http.StatusBadRequest,
+				Title:  "parameter \"deepFilter\" in query is invalid: notbool is an invalid boolean",
+			},
+		},
+		{
+			name: "error - incorrect deepobject query parameter type integer",
+			args: validationArgs{
+				r: newPetstoreRequest(t, http.MethodGet, "/pet/filter?deepFilter[integers]=1.234", nil),
+			},
+			wantErrParam:   "deepFilter",
+			wantErrBody:    `parameter "deepFilter" in query has an error: path integers: value 1.234: an invalid integer: invalid syntax`,
+			wantErrParamIn: "query",
+			wantErrResponse: &ValidationError{
+				Status: http.StatusBadRequest,
+				Title:  "parameter \"deepFilter\" in query is invalid: 1.234 is an invalid integer",
+			},
+		},
+		{
+			name: "error - incorrect deepobject query parameter type integer",
+			args: validationArgs{
+				r: newPetstoreRequest(t, http.MethodGet, "/pet/filter?deepFilter[numbers]=aaa", nil),
+			},
+			wantErrParam:   "deepFilter",
+			wantErrBody:    `parameter "deepFilter" in query has an error: path numbers: value aaa: an invalid number: invalid syntax`,
+			wantErrParamIn: "query",
+			wantErrResponse: &ValidationError{
+				Status: http.StatusBadRequest,
+				Title:  "parameter \"deepFilter\" in query is invalid: aaa is an invalid number",
+			},
+		},
 		{
 			name: "error - missing required query string parameter",
 			args: validationArgs{
@@ -183,8 +245,10 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrParamIn: "query",
 			wantErrBody:    `parameter "status" in query has an error: value is required but missing`,
 			wantErrReason:  "value is required but missing",
-			wantErrResponse: &ValidationError{Status: http.StatusBadRequest,
-				Title: `parameter "status" in query is required`},
+			wantErrResponse: &ValidationError{
+				Status: http.StatusBadRequest,
+				Title:  `parameter "status" in query is required`,
+			},
 		},
 		{
 			name: "error - wrong query string parameter type as integer",
@@ -197,8 +261,10 @@ func getValidationTests(t *testing.T) []*validationTest {
 			// So we'd need to look at the inner one which is a KindInvalidFormat. So just check the error body.
 			wantErrBody: `parameter "ids" in query has an error: path 1: value notAnInt: an invalid integer: invalid syntax`,
 			// TODO: Should we treat query params of the wrong type like a 404 instead of a 400?
-			wantErrResponse: &ValidationError{Status: http.StatusBadRequest,
-				Title: `parameter "ids" in query is invalid: notAnInt is an invalid integer`},
+			wantErrResponse: &ValidationError{
+				Status: http.StatusBadRequest,
+				Title:  `parameter "ids" in query is invalid: notAnInt is an invalid integer`,
+			},
 		},
 		{
 			name: "success - ignores unknown query string parameter",
@@ -215,8 +281,10 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrParamIn: "query",
 			wantErrBody:    `parameter "tags" in query has an error: empty value is not allowed`,
 			wantErrReason:  "empty value is not allowed",
-			wantErrResponse: &ValidationError{Status: http.StatusBadRequest,
-				Title: `parameter "tags" in query is not allowed to be empty`},
+			wantErrResponse: &ValidationError{
+				Status: http.StatusBadRequest,
+				Title:  `parameter "tags" in query is not allowed to be empty`,
+			},
 		},
 		{
 			name: "success - non required query string has empty value, but has AllowEmptyValue",
@@ -246,13 +314,15 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrSchemaReason: "value is not one of the allowed values [\"available\",\"pending\",\"sold\"]",
 			wantErrSchemaPath:   "/0",
 			wantErrSchemaValue:  "available,sold",
-			wantErrResponse: &ValidationError{Status: http.StatusBadRequest,
-				Title: "value is not one of the allowed values [\"available\",\"pending\",\"sold\"]",
+			wantErrResponse: &ValidationError{
+				Status: http.StatusBadRequest,
+				Title:  "value is not one of the allowed values [\"available\",\"pending\",\"sold\"]",
 				Detail: "value available,sold at /0 must be one of: available, pending, sold; " +
 					// TODO: do we really want to use this heuristic to guess
 					//  that they're using the wrong serialization?
 					"perhaps you intended '?status=available&status=sold'",
-				Source: &ValidationErrorSource{Parameter: "status"}},
+				Source: &ValidationErrorSource{Parameter: "status"},
+			},
 		},
 		{
 			name: "error - invalid enum value for query string parameter",
@@ -264,10 +334,12 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrSchemaReason: "value is not one of the allowed values [\"available\",\"pending\",\"sold\"]",
 			wantErrSchemaPath:   "/1",
 			wantErrSchemaValue:  "watdis",
-			wantErrResponse: &ValidationError{Status: http.StatusBadRequest,
+			wantErrResponse: &ValidationError{
+				Status: http.StatusBadRequest,
 				Title:  "value is not one of the allowed values [\"available\",\"pending\",\"sold\"]",
 				Detail: "value watdis at /1 must be one of: available, pending, sold",
-				Source: &ValidationErrorSource{Parameter: "status"}},
+				Source: &ValidationErrorSource{Parameter: "status"},
+			},
 		},
 		{
 			name: "error - invalid enum value, allowing commas (without 'perhaps you intended' recommendation)",
@@ -280,11 +352,13 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrSchemaReason: "value is not one of the allowed values [\"dog\",\"cat\",\"turtle\",\"bird,with,commas\"]",
 			wantErrSchemaPath:   "/1",
 			wantErrSchemaValue:  "fish,with,commas",
-			wantErrResponse: &ValidationError{Status: http.StatusBadRequest,
+			wantErrResponse: &ValidationError{
+				Status: http.StatusBadRequest,
 				Title:  "value is not one of the allowed values [\"dog\",\"cat\",\"turtle\",\"bird,with,commas\"]",
 				Detail: "value fish,with,commas at /1 must be one of: dog, cat, turtle, bird,with,commas",
 				// No 'perhaps you intended' because its the right serialization format
-				Source: &ValidationErrorSource{Parameter: "kind"}},
+				Source: &ValidationErrorSource{Parameter: "kind"},
+			},
 		},
 		{
 			name: "success - valid enum value, allowing commas",
@@ -306,10 +380,12 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrSchemaReason: "value is not one of the allowed values [\"demo\",\"prod\"]",
 			wantErrSchemaPath:   "/",
 			wantErrSchemaValue:  "watdis",
-			wantErrResponse: &ValidationError{Status: http.StatusBadRequest,
+			wantErrResponse: &ValidationError{
+				Status: http.StatusBadRequest,
 				Title:  "value is not one of the allowed values [\"demo\",\"prod\"]",
 				Detail: "value watdis at / must be one of: demo, prod",
-				Source: &ValidationErrorSource{Parameter: "x-environment"}},
+				Source: &ValidationErrorSource{Parameter: "x-environment"},
+			},
 		},
 
 		//
@@ -325,10 +401,12 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrSchemaReason: "value is not one of the allowed values [\"available\",\"pending\",\"sold\"]",
 			wantErrSchemaValue:  "watdis",
 			wantErrSchemaPath:   "/status",
-			wantErrResponse: &ValidationError{Status: http.StatusUnprocessableEntity,
+			wantErrResponse: &ValidationError{
+				Status: http.StatusUnprocessableEntity,
 				Title:  "value is not one of the allowed values [\"available\",\"pending\",\"sold\"]",
 				Detail: "value watdis at /status must be one of: available, pending, sold",
-				Source: &ValidationErrorSource{Pointer: "/status"}},
+				Source: &ValidationErrorSource{Pointer: "/status"},
+			},
 		},
 		{
 			name: "error - missing required object attribute",
@@ -339,9 +417,11 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrSchemaReason: `property "photoUrls" is missing`,
 			wantErrSchemaValue:  map[string]string{"name": "Bahama"},
 			wantErrSchemaPath:   "/photoUrls",
-			wantErrResponse: &ValidationError{Status: http.StatusUnprocessableEntity,
+			wantErrResponse: &ValidationError{
+				Status: http.StatusUnprocessableEntity,
 				Title:  `property "photoUrls" is missing`,
-				Source: &ValidationErrorSource{Pointer: "/photoUrls"}},
+				Source: &ValidationErrorSource{Pointer: "/photoUrls"},
+			},
 		},
 		{
 			name: "error - missing required nested object attribute",
@@ -353,9 +433,11 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrSchemaReason: `property "name" is missing`,
 			wantErrSchemaValue:  map[string]string{},
 			wantErrSchemaPath:   "/category/name",
-			wantErrResponse: &ValidationError{Status: http.StatusUnprocessableEntity,
+			wantErrResponse: &ValidationError{
+				Status: http.StatusUnprocessableEntity,
 				Title:  `property "name" is missing`,
-				Source: &ValidationErrorSource{Pointer: "/category/name"}},
+				Source: &ValidationErrorSource{Pointer: "/category/name"},
+			},
 		},
 		{
 			name: "error - missing required deeply nested object attribute",
@@ -367,9 +449,11 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrSchemaReason: `property "name" is missing`,
 			wantErrSchemaValue:  map[string]string{},
 			wantErrSchemaPath:   "/category/tags/0/name",
-			wantErrResponse: &ValidationError{Status: http.StatusUnprocessableEntity,
+			wantErrResponse: &ValidationError{
+				Status: http.StatusUnprocessableEntity,
 				Title:  `property "name" is missing`,
-				Source: &ValidationErrorSource{Pointer: "/category/tags/0/name"}},
+				Source: &ValidationErrorSource{Pointer: "/category/tags/0/name"},
+			},
 		},
 		{
 			name: "error - wrong attribute type",
@@ -383,9 +467,11 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrSchemaValue:  "http://cat",
 			// TODO: this shouldn't say "or not be present", but this requires recursively resolving
 			//  innerErr.JSONPointer() against e.RequestBody.Content["application/json"].Schema.Value (.Required, .Properties)
-			wantErrResponse: &ValidationError{Status: http.StatusUnprocessableEntity,
+			wantErrResponse: &ValidationError{
+				Status: http.StatusUnprocessableEntity,
 				Title:  "value must be an array",
-				Source: &ValidationErrorSource{Pointer: "/photoUrls"}},
+				Source: &ValidationErrorSource{Pointer: "/photoUrls"},
+			},
 		},
 		{
 			name: "error - missing required object attribute from allOf required overlay",
@@ -399,9 +485,11 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrSchemaOriginReason: `property "photoUrls" is missing`,
 			wantErrSchemaOriginValue:  map[string]string{"name": "Bahama"},
 			wantErrSchemaOriginPath:   "/photoUrls",
-			wantErrResponse: &ValidationError{Status: http.StatusUnprocessableEntity,
+			wantErrResponse: &ValidationError{
+				Status: http.StatusUnprocessableEntity,
 				Title:  `property "photoUrls" is missing`,
-				Source: &ValidationErrorSource{Pointer: "/photoUrls"}},
+				Source: &ValidationErrorSource{Pointer: "/photoUrls"},
+			},
 		},
 		{
 			name: "success - ignores unknown object attribute",
@@ -437,8 +525,10 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrParamIn: "path",
 			wantErrBody:    `parameter "petId" in path has an error: value is required but missing`,
 			wantErrReason:  "value is required but missing",
-			wantErrResponse: &ValidationError{Status: http.StatusBadRequest,
-				Title: `parameter "petId" in path is required`},
+			wantErrResponse: &ValidationError{
+				Status: http.StatusBadRequest,
+				Title:  `parameter "petId" in path is required`,
+			},
 		},
 		{
 			name: "error - wrong path param type",
@@ -450,8 +540,10 @@ func getValidationTests(t *testing.T) []*validationTest {
 			wantErrParseKind:   KindInvalidFormat,
 			wantErrParseValue:  "NotAnInt",
 			wantErrParseReason: "an invalid integer",
-			wantErrResponse: &ValidationError{Status: http.StatusNotFound,
-				Title: `resource not found with "petId" value: NotAnInt`},
+			wantErrResponse: &ValidationError{
+				Status: http.StatusNotFound,
+				Title:  `resource not found with "petId" value: NotAnInt`,
+			},
 		},
 		{
 			name: "success - normal case, with path params",
@@ -470,7 +562,6 @@ func TestValidationHandler_validateRequest(t *testing.T) {
 
 			h, err := buildValidationHandler(tt)
 			req.NoError(err)
-
 			err = h.validateRequest(tt.args.r)
 			req.Equal(tt.wantErr, err != nil)