Skip to content

Commit

Permalink
Fix python-openapi#20 - nullable semantics with $ref, oneOf, anyOf, a…
Browse files Browse the repository at this point in the history
…nd allOf
  • Loading branch information
Andrew Pikler authored and pyckle committed Jan 5, 2023
1 parent 553d606 commit d7261de
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 2 deletions.
8 changes: 6 additions & 2 deletions openapi_schema_validator/_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,12 @@ def include_nullable_validator(
"""
_schema = deepcopy(schema)

# append defaults to trigger nullable validator
if "nullable" not in _schema:
# append defaults to trigger nullable validator, except where $ref in the schema checks null
if "nullable" not in _schema \
and "$ref" not in _schema \
and "oneOf" not in _schema \
and "anyOf" not in _schema \
and "allOf" not in _schema:
_schema.update(
{
"nullable": False,
Expand Down
97 changes: 97 additions & 0 deletions tests/integration/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,103 @@ def test_oneof_discriminator(self, schema_type):
result = validator.validate({"discipline": "other"})
assert False

@pytest.mark.parametrize("is_nullable", [True, False])
def test_nullable_ref(self, is_nullable):
"""
Tests that a field that points to a schema reference is null checked based on the $ref schema rather than
on this schema
:param is_nullable: if the schema is marked as nullable. If not, validate an exception is raised on None
"""
schema = {
"$ref": "#/$defs/Pet",
"$defs": {
"NullableText": {
"type": "string",
"nullable": is_nullable
},
"Pet": {
"properties": {
"testfield": {"$ref": "#/$defs/NullableText"},
},
}
},
}
validator = OAS30Validator(
schema,
format_checker=oas30_format_checker,
)

result = validator.validate({"testfield": "John"})
assert result is None

if is_nullable:
result = validator.validate({"testfield": None})
assert result is None
else:
with pytest.raises(
ValidationError,
match="None for not nullable",
):
validator.validate({"testfield": None})
assert False


@pytest.mark.parametrize(
"schema_type, not_nullable_regex",
[
("oneOf", "None is not valid under any of the given schemas"),
("anyOf", "None is not valid under any of the given schemas"),
("allOf", "None for not nullable")
],
)
@pytest.mark.parametrize("is_nullable", [True, False])
def test_nullable_schema_combos(self, is_nullable, schema_type, not_nullable_regex):
"""
This test ensures that nullablilty semantics are correct for oneOf, anyOf and allOf
Specifically the nullability should be done based on the
:param is_nullable: if the schema is marked as nullable. If not, validate an exception is raised on None
:param schema_type: the schema type to validate
:param not_nullable_regex: the expected raised exception if fields are marked as not nullable
"""
schema = {
"$ref": "#/$defs/Pet",
"$defs": {
"NullableText": {
"type": "string",
"nullable": False if schema_type == "oneOf" else is_nullable
},
"NullableEnum": {
"type": "string",
"nullable": is_nullable,
"enum": ["John", "Alice", None]
},
"Pet": {
"properties": {
"testfield": {
schema_type: [
{"$ref": "#/$defs/NullableText"},
{"$ref": "#/$defs/NullableEnum"},
]
}
},
}
},
}
validator = OAS30Validator(
schema,
format_checker=oas30_format_checker,
)

if is_nullable:
result = validator.validate({"testfield": None})
assert result is None
else:
with pytest.raises(
ValidationError,
match=not_nullable_regex
):
validator.validate({"testfield": None})
assert False

class TestOAS31ValidatorValidate:
@pytest.mark.parametrize(
Expand Down

0 comments on commit d7261de

Please sign in to comment.