Skip to content
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
26 changes: 26 additions & 0 deletions test/validation_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,5 +219,31 @@ def test_validation_timeseries():
schema_type="plant/energy_resource",
)

def test_restrictive_rejects_additional_properties_in_ref_schemas():
"""Test that restrictive validation catches additional properties in $ref-resolved schemas.

The site schema is loaded via $ref from wind_energy_system.yaml.
Without the fix to propagate enforcement through the registry,
extra properties in $ref-resolved schemas would pass silently.
"""
plant_reference_path = Path(windIO.plant_ex.__file__).parent
data = windIO.load_yaml(
plant_reference_path / "wind_energy_system/IEA37_case_study_1_2_wind_energy_system.yaml"
)

# Inject an extra property into site, which is resolved via $ref
data["site"]["not_in_schema"] = "should be rejected"

# restrictive=True (default) must reject it
with pytest.raises(
jsonschema.exceptions.ValidationError,
match="Additional properties are not allowed.*'not_in_schema'",
):
windIO.validate(data, "plant/wind_energy_system", restrictive=True)

# restrictive=False must allow it
windIO.validate(data, "plant/wind_energy_system", restrictive=False)


if __name__ == "__main__":
test_validate_raise()
27 changes: 17 additions & 10 deletions windIO/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,20 @@
from .schemas import schemaPath, schema_validation_error_formatter


def retrieve_yaml(uri: str):
if not uri.endswith(".yaml"):
raise NoSuchResource(ref=uri)
uri = uri.removeprefix("windIO/")
path = schemaPath / Path(uri)
contents = load_yaml(path)
return Resource.from_contents(contents)
def _make_retrieve(restrictive=False):
def retrieve(uri: str):
if not uri.endswith(".yaml"):
raise NoSuchResource(ref=uri)
uri = uri.removeprefix("windIO/")
path = schemaPath / Path(uri)
contents = load_yaml(path)
if restrictive:
_enforce_no_additional_properties(contents)
return Resource.from_contents(contents)
return retrieve


registry = Registry(retrieve=retrieve_yaml)
registry = Registry(retrieve=_make_retrieve())


def _enforce_no_additional_properties(schema):
Expand Down Expand Up @@ -93,11 +97,14 @@ def validate(
schema = load_yaml(schema_file)
if restrictive:
schema = _enforce_no_additional_properties(schema)
reg = Registry(retrieve=_make_retrieve(restrictive=True))
else:
reg = registry

if defaults:
_jsonschema_validate_modified(data, schema, cls = DefaultValidatingDraft7Validator, registry=registry)
_jsonschema_validate_modified(data, schema, cls = DefaultValidatingDraft7Validator, registry=reg)
else:
_jsonschema_validate_modified(data, schema, registry=registry)
_jsonschema_validate_modified(data, schema, registry=reg)

return data

Expand Down