Skip to content
This repository has been archived by the owner on Nov 19, 2023. It is now read-only.

Commit

Permalink
feat: adding support for request validation
Browse files Browse the repository at this point in the history
  • Loading branch information
maticardenas committed Oct 14, 2023
1 parent 08d9449 commit a3430a1
Show file tree
Hide file tree
Showing 14 changed files with 482 additions and 91 deletions.
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ from openapi_tester import SchemaTester
schema_tester = SchemaTester(schema_file_path="./schemas/publishedSpecs.yaml")
```

Once you've instantiated a tester, you can use it to test responses:
Once you've instantiated a tester, you can use it to test responses and request bodies:

```python
from openapi_tester.schema_tester import SchemaTester
Expand All @@ -53,6 +53,12 @@ def test_response_documentation(client):
response = client.get('api/v1/test/1')
assert response.status_code == 200
schema_tester.validate_response(response=response)


def test_request_documentation(client):
response = client.get('api/v1/test/1')
assert response.status_code == 200
schema_tester.validate_request(response=response)
```

If you are using the Django testing framework, you can create a base `APITestCase` that incorporates schema validation:
Expand Down Expand Up @@ -188,11 +194,11 @@ In case of issues with the schema itself, the validator will raise the appropria

The library includes an `OpenAPIClient`, which extends Django REST framework's
[`APIClient` class](https://www.django-rest-framework.org/api-guide/testing/#apiclient).
If you wish to validate each response against OpenAPI schema when writing
If you wish to validate each request and response against OpenAPI schema when writing
unit tests - `OpenAPIClient` is what you need!

To use `OpenAPIClient` simply pass `SchemaTester` instance that should be used
to validate responses and then use it like regular Django testing client:
to validate requests and responses and then use it like regular Django testing client:

```python
schema_tester = SchemaTester()
Expand Down
6 changes: 6 additions & 0 deletions openapi_tester/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@ def __init__(
def request(self, **kwargs) -> Response: # type: ignore[override]
"""Validate fetched response against given OpenAPI schema."""
response = super().request(**kwargs)
if self._is_successful_response(response):
self.schema_tester.validate_request(response)
self.schema_tester.validate_response(response)
return response

@staticmethod
def _is_successful_response(response: Response) -> bool:
return response.status_code < 400

@staticmethod
def _schema_tester_factory() -> SchemaTester:
"""Factory of default ``SchemaTester`` instances."""
Expand Down
12 changes: 6 additions & 6 deletions openapi_tester/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
INVALID_PATTERN_ERROR = "String pattern is not valid regex: {pattern}"
VALIDATE_ENUM_ERROR = "Expected: a member of the enum {enum}\n\nReceived: {received}"
VALIDATE_TYPE_ERROR = 'Expected: {article} "{type}" type value\n\nReceived: {received}'
VALIDATE_MULTIPLE_OF_ERROR = "The response value {data} should be a multiple of {multiple}"
VALIDATE_MINIMUM_ERROR = "The response value {data} is lower than the specified minimum of {minimum}"
VALIDATE_MAXIMUM_ERROR = "The response value {data} exceeds the maximum allowed value of {maximum}"
VALIDATE_MULTIPLE_OF_ERROR = "The value {data} should be a multiple of {multiple}"
VALIDATE_MINIMUM_ERROR = "The value {data} is lower than the specified minimum of {minimum}"
VALIDATE_MAXIMUM_ERROR = "The value {data} exceeds the maximum allowed value of {maximum}"
VALIDATE_MIN_LENGTH_ERROR = 'The length of "{data}" is shorter than the specified minimum length of {min_length}'
VALIDATE_MAX_LENGTH_ERROR = 'The length of "{data}" exceeds the specified maximum length of {max_length}'
VALIDATE_MIN_ARRAY_LENGTH_ERROR = (
Expand All @@ -32,9 +32,9 @@
)
VALIDATE_UNIQUE_ITEMS_ERROR = "The array {data} must contain unique items only"
VALIDATE_NONE_ERROR = "Received a null value for a non-nullable schema object"
VALIDATE_MISSING_RESPONSE_KEY_ERROR = 'The following property is missing in the response data: "{missing_key}"'
VALIDATE_EXCESS_RESPONSE_KEY_ERROR = (
'The following property was found in the response, but is missing from the schema definition: "{excess_key}"'
VALIDATE_MISSING_KEY_ERROR = 'The following property is missing in the {http_message} data: "{missing_key}"'
VALIDATE_EXCESS_KEY_ERROR = (
'The following property was found in the {http_message}, but is missing from the schema definition: "{excess_key}"'
)
VALIDATE_WRITE_ONLY_RESPONSE_KEY_ERROR = (
'The following property was found in the response, but is documented as being "writeOnly": "{write_only_key}"'
Expand Down
2 changes: 2 additions & 0 deletions openapi_tester/loaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ def set_schema(self, schema: dict) -> None:
"""
de_referenced_schema = self.de_reference_schema(schema)
self.validate_schema(de_referenced_schema)

self.schema = self.normalize_schema_paths(de_referenced_schema)

@cached_property
Expand Down Expand Up @@ -245,6 +246,7 @@ class StaticSchemaLoader(BaseSchemaLoader):

def __init__(self, path: str, field_key_map: dict[str, str] | None = None):
super().__init__(field_key_map=field_key_map)

self.path = path if not isinstance(path, pathlib.PosixPath) else str(path)

def load_schema(self) -> dict[str, Any]:
Expand Down
Loading

0 comments on commit a3430a1

Please sign in to comment.