Skip to content

Commit

Permalink
fix: KeyError during creating endpoint strategy if it contains a re…
Browse files Browse the repository at this point in the history
…ference

Ref: #612
  • Loading branch information
Stranger6667 committed Jun 15, 2020
1 parent 3cb0a7b commit 2cda398
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 10 deletions.
2 changes: 2 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Fixed
~~~~~

- Tests with invalid schemas are marked as failed when ``hypothesis-jsonschema>=0.16`` is installed. `#614`_
- ``KeyError`` during creating endpoint strategy if it contains a reference. `#612`_

Changed
~~~~~~~
Expand Down Expand Up @@ -1110,6 +1111,7 @@ Fixed
.. _0.2.0: https://github.com/kiwicom/schemathesis/compare/v0.1.0...v0.2.0

.. _#614: https://github.com/kiwicom/schemathesis/issues/614
.. _#612: https://github.com/kiwicom/schemathesis/issues/612
.. _#600: https://github.com/kiwicom/schemathesis/issues/600
.. _#599: https://github.com/kiwicom/schemathesis/issues/599
.. _#596: https://github.com/kiwicom/schemathesis/issues/596
Expand Down
3 changes: 2 additions & 1 deletion src/schemathesis/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ class EndpointDefinition:
"""

raw: Dict[str, Any] = attr.ib() # pragma: no mutate
resolved: Dict[str, Any] = attr.ib() # pragma: no mutate
scope: str = attr.ib() # pragma: no mutate


Expand Down Expand Up @@ -288,7 +289,7 @@ def get_stateful_tests(self, response: GenericResponse, stateful: Optional[str])
return self.schema.get_stateful_tests(response, self, stateful)

def get_hypothesis_conversions(self, location: str) -> Optional[Callable]:
definitions = [item for item in self.definition.raw.get("parameters", []) if item["in"] == location]
definitions = [item for item in self.definition.resolved.get("parameters", []) if item["in"] == location]
if definitions:
return self.schema.get_hypothesis_conversion(definitions)
return None
Expand Down
6 changes: 3 additions & 3 deletions src/schemathesis/specs/openapi/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def get_all_endpoints(self) -> Generator[Endpoint, None, None]:
parameters = itertools.chain(resolved_definition.get("parameters", ()), common_parameters)
# To prevent recursion errors we need to pass not resolved schema as well
# It could be used for response validation
raw_definition = EndpointDefinition(raw_methods[method], scope)
raw_definition = EndpointDefinition(raw_methods[method], resolved_definition, scope)
yield self.make_endpoint(full_path, method, parameters, resolved_definition, raw_definition)
except (KeyError, AttributeError, jsonschema.exceptions.RefResolutionError):
raise InvalidSchema("Schema parsing failed. Please check your schema.")
Expand Down Expand Up @@ -160,7 +160,7 @@ def _group_endpoints_by_operation_id(self) -> Generator[Tuple[str, Endpoint], No
if method not in self.operations or "operationId" not in resolved_definition:
continue
parameters = itertools.chain(resolved_definition.get("parameters", ()), common_parameters)
raw_definition = EndpointDefinition(raw_methods[method], scope)
raw_definition = EndpointDefinition(raw_methods[method], resolved_definition, scope)
yield resolved_definition["operationId"], self.make_endpoint(
full_path, method, parameters, resolved_definition, raw_definition
)
Expand All @@ -179,7 +179,7 @@ def get_endpoint_by_reference(self, reference: str) -> Endpoint:
_, methods = self.resolver.resolve(parent_ref)
common_parameters = get_common_parameters(methods)
parameters = itertools.chain(resolved_definition.get("parameters", ()), common_parameters)
raw_definition = EndpointDefinition(data, scope)
raw_definition = EndpointDefinition(data, resolved_definition, scope)
return self.make_endpoint(full_path, method, parameters, resolved_definition, raw_definition)


Expand Down
4 changes: 3 additions & 1 deletion test/runner/test_checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@


def make_case(schema: BaseSchema, definition: Dict[str, Any]) -> models.Case:
endpoint = models.Endpoint("/path", "GET", definition=EndpointDefinition(definition, None), schema=schema)
endpoint = models.Endpoint(
"/path", "GET", definition=EndpointDefinition(definition, definition, None), schema=schema
)
return models.Case(endpoint)


Expand Down
46 changes: 46 additions & 0 deletions test/test_dereferencing.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,52 @@ def test_complex_dereference(testdir, complex_schema):
"summary": "Test",
"tags": ["ancillaries"],
},
{
"requestBody": {
"content": {
"application/json": {
"schema": {
"additionalProperties": False,
"description": "Test",
"properties": {
"profile": {
"additionalProperties": False,
"description": "Test",
"properties": {"id": {"type": "integer"}},
"required": ["id"],
"type": "object",
},
"username": {"type": "string"},
},
"required": ["username", "profile"],
"type": "object",
}
}
},
"description": "Test.",
"required": True,
},
"responses": {
"default": {
"content": {
"application/json": {
"schema": {
"additionalProperties": False,
"properties": {
"key": {"anyOf": [{"type": "string"}, {"type": "null"}]},
"referenced": {"anyOf": [{"type": "string"}, {"type": "null"}]},
},
"required": ["key", "referenced"],
"type": "object",
}
}
},
"description": "Probably an error",
}
},
"summary": "Test",
"tags": ["ancillaries"],
},
scope=f"{path.as_uri()}/root/paths/teapot.yaml#/TeapotCreatePath",
),
body={
Expand Down
25 changes: 25 additions & 0 deletions test/test_direct_access.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,28 @@ def test_as_strategy(swagger_20):
strategy = endpoint.as_strategy()
assert isinstance(strategy, st.SearchStrategy)
assert strategy.example() == Case(endpoint)


@pytest.mark.filterwarnings("ignore:.*method is good for exploring strategies.*")
def test_reference_in_path():
raw_schema = {
"openapi": "3.0.0",
"info": {"title": "Blank API", "version": "1.0"},
"servers": [{"url": "http://localhost/api"}],
"paths": {
"/{key}": {
"get": {
"parameters": [{"$ref": "#/components/parameters/PathParameter"}],
"responses": {"200": {"description": "OK"}},
}
}
},
"components": {
"parameters": {
"PathParameter": {"in": "path", "name": "key", "required": True, "schema": {"type": "string"},}
}
},
}
schema = schemathesis.from_dict(raw_schema)
strategy = schema["/api/{key}"]["GET"].as_strategy()
assert isinstance(strategy.example().path_parameters["key"], str)
10 changes: 5 additions & 5 deletions test/test_hypothesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@


def make_endpoint(schema, **kwargs) -> Endpoint:
return Endpoint("/users", "POST", definition=EndpointDefinition({}, "foo"), schema=schema, **kwargs)
return Endpoint("/users", "POST", definition=EndpointDefinition({}, {}, "foo"), schema=schema, **kwargs)


@pytest.mark.parametrize("name", sorted(PARAMETERS))
Expand All @@ -38,7 +38,7 @@ def test_no_body_in_get(swagger_20):
endpoint = Endpoint(
path="/api/success",
method="GET",
definition=EndpointDefinition({}, "foo"),
definition=EndpointDefinition({}, {}, "foo"),
schema=swagger_20,
query={
"required": ["name"],
Expand All @@ -55,7 +55,7 @@ def test_invalid_body_in_get(swagger_20):
endpoint = Endpoint(
path="/foo",
method="GET",
definition=EndpointDefinition({}, "foo"),
definition=EndpointDefinition({}, {}, "foo"),
schema=swagger_20,
body={"required": ["foo"], "type": "object", "properties": {"foo": {"type": "string"}}},
)
Expand All @@ -69,7 +69,7 @@ def test_invalid_body_in_get_disable_validation(simple_schema):
endpoint = Endpoint(
path="/foo",
method="GET",
definition=EndpointDefinition({}, "foo"),
definition=EndpointDefinition({}, {}, "foo"),
schema=schema,
body={"required": ["foo"], "type": "object", "properties": {"foo": {"type": "string"}}},
)
Expand Down Expand Up @@ -160,7 +160,7 @@ def test_valid_headers(base_url, swagger_20):
endpoint = Endpoint(
"/api/success",
"GET",
definition=EndpointDefinition({}, "foo"),
definition=EndpointDefinition({}, {}, "foo"),
schema=swagger_20,
base_url=base_url,
headers={
Expand Down

0 comments on commit 2cda398

Please sign in to comment.