Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions responses/validate_body.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func (v *responseBodyValidator) ValidateResponseBodyWithPathItem(request *http.R
if foundResponse != nil {
if v.options.ValidateResponseBody && foundResponse.Content != nil { // only validate if we have content types.
// check content type has been defined in the contract
if mediaType, ok := foundResponse.Content.Get(mediaTypeSting); ok {
if mediaType, ok := v.extractContentType(mediaTypeSting, foundResponse); ok {
validationErrors = append(validationErrors,
v.checkResponseSchema(request, response, contentType, mediaType, operation)...)
} else {
Expand All @@ -98,7 +98,7 @@ func (v *responseBodyValidator) ValidateResponseBodyWithPathItem(request *http.R
// check content type has been defined in the contract
if !v.options.ValidateResponseBody {
foundResponse = operation.Responses.Default
} else if mediaType, ok := operation.Responses.Default.Content.Get(mediaTypeSting); ok {
} else if mediaType, ok := v.extractContentType(mediaTypeSting, operation.Responses.Default); ok {
foundResponse = operation.Responses.Default
validationErrors = append(validationErrors,
v.checkResponseSchema(request, response, contentType, mediaType, operation)...)
Expand Down Expand Up @@ -239,3 +239,21 @@ func (v *responseBodyValidator) checkResponseSchema(

return validationErrors
}

func (v *responseBodyValidator) extractContentType(contentType string, response *v3.Response) (*v3.MediaType, bool) {
mediaType, ok := response.Content.Get(contentType)
if ok {
return mediaType, true
}
ctMediaRange := strings.SplitN(contentType, "/", 2)
for contentPair := response.Content.First(); contentPair != nil; contentPair = contentPair.Next() {
s := contentPair.Key()
mediaTypeValue := contentPair.Value()
opMediaRange := strings.SplitN(s, "/", 2)
if (opMediaRange[0] == "*" || opMediaRange[0] == ctMediaRange[0]) &&
(opMediaRange[1] == "*" || opMediaRange[1] == ctMediaRange[1]) {
return mediaTypeValue, true
}
}
return nil, false
}
294 changes: 294 additions & 0 deletions responses/validate_body_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,153 @@ paths:
assert.Len(t, errors, 0)
}

func TestValidateBody_ValidBasicSchema_WithContentTypeWildcards(t *testing.T) {
tb := newvalidateResponseTestBed(
t,
[]byte(`openapi: 3.1.0
paths:
/burgers/createBurger:
post:
responses:
'200':
content:
"*/*":
schema:
type: object
properties:
name:
type: string
patties:
type: integer
vegetarian:
type: boolean`,
),
)

req, res := tb.makeRequestWithReponse(
t,
http.MethodPost,
"/burgers/createBurger",
func(w http.ResponseWriter, r *http.Request) {
bodyBytes, err := json.Marshal(map[string]interface{}{
"name": "Big Mac",
"patties": 2,
"vegetarian": false,
})

require.NoError(t, err, "failed to marshal body")

w.Header().Set(helpers.ContentTypeHeader, helpers.JSONContentType)
w.WriteHeader(http.StatusOK)
_, _ = w.Write(bodyBytes)
},
)

// validate!
valid, errors := tb.responseBodyValidator.ValidateResponseBody(req, res)

assert.True(t, valid)
assert.Len(t, errors, 0)
}

func TestValidateBody_ValidBasicSchema_WithContentTypeWildcardEnd(t *testing.T) {
tb := newvalidateResponseTestBed(
t,
[]byte(`openapi: 3.1.0
paths:
/burgers/createBurger:
post:
responses:
'200':
content:
"application/*":
schema:
type: object
properties:
name:
type: string
patties:
type: integer
vegetarian:
type: boolean`,
),
)

req, res := tb.makeRequestWithReponse(
t,
http.MethodPost,
"/burgers/createBurger",
func(w http.ResponseWriter, r *http.Request) {
bodyBytes, err := json.Marshal(map[string]interface{}{
"name": "Big Mac",
"patties": 2,
"vegetarian": false,
})

require.NoError(t, err, "failed to marshal body")

w.Header().Set(helpers.ContentTypeHeader, helpers.JSONContentType)
w.WriteHeader(http.StatusOK)
_, _ = w.Write(bodyBytes)
},
)

// validate!
valid, errors := tb.responseBodyValidator.ValidateResponseBody(req, res)

assert.True(t, valid)
assert.Len(t, errors, 0)
}

func TestValidateBody_ValidBasicSchema_WithContentTypeWildcardStart(t *testing.T) {
tb := newvalidateResponseTestBed(
t,
[]byte(`openapi: 3.1.0
paths:
/burgers/createBurger:
post:
responses:
'200':
content:
"*/json":
schema:
type: object
properties:
name:
type: string
patties:
type: integer
vegetarian:
type: boolean`,
),
)

req, res := tb.makeRequestWithReponse(
t,
http.MethodPost,
"/burgers/createBurger",
func(w http.ResponseWriter, r *http.Request) {
bodyBytes, err := json.Marshal(map[string]interface{}{
"name": "Big Mac",
"patties": 2,
"vegetarian": false,
})

require.NoError(t, err, "failed to marshal body")

w.Header().Set(helpers.ContentTypeHeader, helpers.JSONContentType)
w.WriteHeader(http.StatusOK)
_, _ = w.Write(bodyBytes)
},
)

// validate!
valid, errors := tb.responseBodyValidator.ValidateResponseBody(req, res)

assert.True(t, valid)
assert.Len(t, errors, 0)
}

func TestValidateBody_ValidBasicSchemaUsingDefault(t *testing.T) {
tb := newvalidateResponseTestBed(
t,
Expand Down Expand Up @@ -994,6 +1141,153 @@ paths:
assert.Len(t, errors, 0)
}

func TestValidateBody_ValidBasicSchemaUsingDefault_WithContentTypeWildcards(t *testing.T) {
tb := newvalidateResponseTestBed(
t,
[]byte(`openapi: 3.1.0
paths:
/burgers/createBurger:
post:
responses:
default:
content:
"*/*":
schema:
type: object
properties:
name:
type: string
patties:
type: integer
vegetarian:
type: boolean`,
),
)

req, res := tb.makeRequestWithReponse(
t,
http.MethodPost,
"/burgers/createBurger",
func(w http.ResponseWriter, r *http.Request) {
bodyBytes, err := json.Marshal(map[string]interface{}{
"name": "Big Mac",
"patties": 2,
"vegetarian": false,
})

require.NoError(t, err, "failed to marshal body")

w.Header().Set(helpers.ContentTypeHeader, helpers.JSONContentType)
w.WriteHeader(http.StatusOK)
_, _ = w.Write(bodyBytes)
},
)

// validate!
valid, errors := tb.responseBodyValidator.ValidateResponseBody(req, res)

assert.True(t, valid)
assert.Len(t, errors, 0)
}

func TestValidateBody_ValidBasicSchemaUsingDefault_WithContentTypeWildcardEnd(t *testing.T) {
tb := newvalidateResponseTestBed(
t,
[]byte(`openapi: 3.1.0
paths:
/burgers/createBurger:
post:
responses:
default:
content:
"application/*":
schema:
type: object
properties:
name:
type: string
patties:
type: integer
vegetarian:
type: boolean`,
),
)

req, res := tb.makeRequestWithReponse(
t,
http.MethodPost,
"/burgers/createBurger",
func(w http.ResponseWriter, r *http.Request) {
bodyBytes, err := json.Marshal(map[string]interface{}{
"name": "Big Mac",
"patties": 2,
"vegetarian": false,
})

require.NoError(t, err, "failed to marshal body")

w.Header().Set(helpers.ContentTypeHeader, helpers.JSONContentType)
w.WriteHeader(http.StatusOK)
_, _ = w.Write(bodyBytes)
},
)

// validate!
valid, errors := tb.responseBodyValidator.ValidateResponseBody(req, res)

assert.True(t, valid)
assert.Len(t, errors, 0)
}

func TestValidateBody_ValidBasicSchemaUsingDefault_WithContentTypeWildcardStart(t *testing.T) {
tb := newvalidateResponseTestBed(
t,
[]byte(`openapi: 3.1.0
paths:
/burgers/createBurger:
post:
responses:
default:
content:
"*/json":
schema:
type: object
properties:
name:
type: string
patties:
type: integer
vegetarian:
type: boolean`,
),
)

req, res := tb.makeRequestWithReponse(
t,
http.MethodPost,
"/burgers/createBurger",
func(w http.ResponseWriter, r *http.Request) {
bodyBytes, err := json.Marshal(map[string]interface{}{
"name": "Big Mac",
"patties": 2,
"vegetarian": false,
})

require.NoError(t, err, "failed to marshal body")

w.Header().Set(helpers.ContentTypeHeader, helpers.JSONContentType)
w.WriteHeader(http.StatusOK)
_, _ = w.Write(bodyBytes)
},
)

// validate!
valid, errors := tb.responseBodyValidator.ValidateResponseBody(req, res)

assert.True(t, valid)
assert.Len(t, errors, 0)
}

func TestValidateBody_InvalidBasicSchemaUsingDefault_MissingContentType(t *testing.T) {
tb := newvalidateResponseTestBed(
t,
Expand Down
Loading