Skip to content

Responses schema validation #177

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 5, 2022
Merged
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
33 changes: 33 additions & 0 deletions openapi_spec_validator/validation/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,10 @@ def _iter_operation_errors(
)
self.operation_ids_registry.append(operation_id)

if "responses" in operation:
responses = operation / "responses"
yield from self._iter_responses_errors(responses)

names = []

parameters = None
Expand All @@ -161,6 +165,35 @@ def _iter_operation_errors(
)
return

def _iter_responses_errors(
self, responses: Spec
) -> Iterator[ValidationError]:
for response_code, response in responses.items():
yield from self._iter_response_errors(response_code, response)

def _iter_response_errors(
self, response_code: str, response: Spec
) -> Iterator[ValidationError]:
# openapi 2
if "schema" in response:
schema = response / "schema"
yield from self._iter_schema_errors(schema)
# openapi 3
if "content" in response:
content = response / "content"
yield from self._iter_content_errors(content)

def _iter_content_errors(self, content: Spec) -> Iterator[ValidationError]:
for mimetype, media_type in content.items():
yield from self._iter_media_type_errors(mimetype, media_type)

def _iter_media_type_errors(
self, mimetype: str, media_type: Spec
) -> Iterator[ValidationError]:
if "schema" in media_type:
schema = media_type / "schema"
yield from self._iter_schema_errors(schema)

def _get_path_param_names(self, params: Spec) -> Iterator[str]:
for param in params:
if param["in"] == "path":
Expand Down
101 changes: 101 additions & 0 deletions tests/integration/data/v2.0/missing-reference.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
swagger: "2.0"
info:
version: 1.0.0
title: Swagger Petstore
license:
name: MIT
host: petstore.swagger.io
basePath: /v1
schemes:
- http
consumes:
- application/json
produces:
- application/json
paths:
/pets:
get:
summary: List all pets
operationId: listPets
tags:
- pets
parameters:
- name: limit
in: query
description: How many items to return at one time (max 100)
required: false
type: integer
format: int32
responses:
200:
description: A paged array of pets
headers:
x-next:
type: string
description: A link to the next page of responses
schema:
$ref: 'definitions/Pets'
default:
description: unexpected error
schema:
$ref: '#/definitions/'
post:
summary: Create a pet
operationId: createPets
tags:
- pets
responses:
'201':
description: Null response
default:
description: unexpected error
schema:
$ref: '#/definitions/Error'
/pets/{petId}:
get:
summary: Info for a specific pet
operationId: showPetById
tags:
- pets
parameters:
- name: petId
in: path
required: true
description: The id of the pet to retrieve
type: string
responses:
'200':
description: Expected response to a valid request
schema:
$ref: '#/definitions/Pets'
default:
description: unexpected error
schema:
$ref: '#/definitions/Error'
definitions:
Pet:
required:
- id
- name
properties:
id:
type: integer
format: int64
name:
type: string
tag:
type: string
Pets:
type: array
items:
$ref: '#/definitions/Pet'
Error:
required:
- code
- message
properties:
code:
type: integer
format: int32
message:
type: string
52 changes: 51 additions & 1 deletion tests/integration/validation/test_validators.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,58 @@
import pytest
from jsonschema.exceptions import RefResolutionError

from openapi_spec_validator.validation.exceptions import OpenAPIValidationError


class TestLocalOpenAPIv2Validator:

LOCAL_SOURCE_DIRECTORY = "data/v2.0/"

def local_test_suite_file_path(self, test_file):
return f"{self.LOCAL_SOURCE_DIRECTORY}{test_file}"

@pytest.mark.parametrize(
"spec_file",
[
"petstore.yaml",
],
)
def test_valid(self, factory, validator_v2, spec_file):
spec_path = self.local_test_suite_file_path(spec_file)
spec = factory.spec_from_file(spec_path)
spec_url = factory.spec_file_url(spec_path)

return validator_v2.validate(spec, spec_url=spec_url)

@pytest.mark.parametrize(
"spec_file",
[
"empty.yaml",
],
)
def test_validation_failed(self, factory, validator_v2, spec_file):
spec_path = self.local_test_suite_file_path(spec_file)
spec = factory.spec_from_file(spec_path)
spec_url = factory.spec_file_url(spec_path)

with pytest.raises(OpenAPIValidationError):
validator_v2.validate(spec, spec_url=spec_url)

@pytest.mark.parametrize(
"spec_file",
[
"missing-reference.yaml",
],
)
def test_ref_failed(self, factory, validator_v2, spec_file):
spec_path = self.local_test_suite_file_path(spec_file)
spec = factory.spec_from_file(spec_path)
spec_url = factory.spec_file_url(spec_path)

with pytest.raises(RefResolutionError):
validator_v2.validate(spec, spec_url=spec_url)


class TestLocalOpenAPIv30Validator:

LOCAL_SOURCE_DIRECTORY = "data/v3.0/"
Expand Down Expand Up @@ -31,7 +81,7 @@ def test_valid(self, factory, validator_v30, spec_file):
"empty.yaml",
],
)
def test_falied(self, factory, validator_v30, spec_file):
def test_failed(self, factory, validator_v30, spec_file):
spec_path = self.local_test_suite_file_path(spec_file)
spec = factory.spec_from_file(spec_path)
spec_url = factory.spec_file_url(spec_path)
Expand Down