Skip to content

Commit

Permalink
test: provide test for validate_json()
Browse files Browse the repository at this point in the history
Additionally, replace relative import
with absolute import in `test_util.py`
The relative import was referring to
an ancestor. See https://docs.astral.sh/ruff/rules/relative-imports/
  • Loading branch information
candleindark committed Jan 30, 2025
1 parent 5930e7f commit d5169cf
Showing 1 changed file with 160 additions and 4 deletions.
164 changes: 160 additions & 4 deletions dandischema/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
from contextlib import nullcontext
from typing import Dict, List, Optional, Union, cast
from typing import Any, Dict, List, Optional, Union, cast

from jsonschema.exceptions import SchemaError, ValidationError
from jsonschema.protocols import Validator as JsonschemaValidator
from jsonschema.validators import Draft7Validator, Draft202012Validator
import pytest

from dandischema.utils import jsonschema_validator

from ..utils import (
from dandischema.exceptions import JsonschemaValidationError
from dandischema.utils import (
_ensure_newline,
jsonschema_validator,
name2title,
sanitize_value,
strip_top_level_optional,
validate_json,
version2tuple,
)

Expand Down Expand Up @@ -255,3 +256,158 @@ def test_invalid_schema_raises_schema_error(self) -> None:
}
with pytest.raises(SchemaError):
jsonschema_validator(invalid_schema, check_format=False)


# --------------------------------------------------------------------
# FIXTURES that build on top of existing schema fixtures
# --------------------------------------------------------------------
@pytest.fixture
def draft7_schema_with_version(draft7_schema: Dict[str, Any]) -> Dict[str, Any]:
"""
Copies the existing Draft 7 fixture and adds a "schemaVersion" field.
"""
schema = draft7_schema.copy()
schema["schemaVersion"] = "0.6.1"
return schema


@pytest.fixture
def draft202012_schema_with_version(
draft202012_schema: Dict[str, Any]
) -> Dict[str, Any]:
"""
Copies the existing Draft 2020-12 fixture and adds a "schemaVersion" field.
"""
schema = draft202012_schema.copy()
schema["schemaVersion"] = "0.6.9"
return schema


# --------------------------------------------------------------------
# TEST CLASS for validate_json
# --------------------------------------------------------------------
class TestValidateJson:
@pytest.mark.parametrize(
"schema_fixture",
[
"draft7_schema", # Doesn't have "schemaVersion"
"draft202012_schema", # Doesn't have "schemaVersion"
"draft202012_format_schema", # Doesn't have "schemaVersion"
"schema_no_dollar_schema", # Also doesn't have "schemaVersion"
],
ids=[
"draft7_schema_no_schemaVersion",
"draft202012_schema_no_schemaVersion",
"draft202012_format_schema_no_schemaVersion",
"schema_no_dollar_schema_no_schemaVersion",
],
)
def test_missing_schema_version(
self, request: pytest.FixtureRequest, schema_fixture: str
) -> None:
"""
Test that a ValueError is raised if the schema lacks a "schemaVersion" field.
We use existing fixtures that do not define "schemaVersion".
"""
schema = request.getfixturevalue(schema_fixture) # Retrieve the fixture by name
with pytest.raises(ValueError):
validate_json({}, schema)

@pytest.mark.parametrize(
"schema_fixture",
[
# We pass a fixture with a valid schemaVersion,
# but we break the schema definition
pytest.param("draft7_schema_with_version", id="draft7_schema_invalid"),
pytest.param(
"draft202012_schema_with_version", id="draft202012_schema_invalid"
),
],
)
def test_invalid_schema(
self, request: pytest.FixtureRequest, schema_fixture: str
) -> None:
"""
Test that a SchemaError is raised if the schema is invalid.
We intentionally corrupt the "type" field to a non-string value.
"""
schema = request.getfixturevalue(schema_fixture)
schema["type"] = 123 # Invalid: 'type' must be a string or array in JSON Schema

with pytest.raises(SchemaError):
validate_json({}, schema)

@pytest.mark.parametrize(
("schema_fixture", "instance"),
[
pytest.param(
"draft7_schema_with_version",
{"name": "Alice"},
id="draft7_schema_valid_instance",
),
pytest.param(
"draft202012_schema_with_version",
{"title": "My Title"},
id="draft202012_schema_valid_instance",
),
],
)
def test_valid_instance(
self,
request: pytest.FixtureRequest,
schema_fixture: str,
instance: Dict[str, Any],
) -> None:
"""
Test that a valid instance does not raise any exceptions.
"""
schema = request.getfixturevalue(schema_fixture)
validate_json(instance, schema) # No exception expected

@pytest.mark.parametrize(
("schema_fixture", "instance"),
[
pytest.param(
"draft7_schema_with_version",
{}, # Missing required "name"
id="draft7_schema_missing_name",
),
pytest.param(
"draft7_schema_with_version",
{"name": 123}, # Wrong type
id="draft7_schema_wrong_type_for_name",
),
pytest.param(
"draft202012_schema_with_version",
{}, # Missing required "title"
id="draft202012_schema_missing_title",
),
pytest.param(
"draft202012_schema_with_version",
{"title": 999}, # Wrong type
id="draft202012_schema_wrong_type_for_title",
),
],
)
def test_invalid_instance(
self,
request: pytest.FixtureRequest,
schema_fixture: str,
instance: Dict[str, Any],
) -> None:
"""
Test that an invalid instance raises a JsonschemaValidationError.
Also assert that the raised error contains a non-empty list of errors.
"""
schema = request.getfixturevalue(schema_fixture)

with pytest.raises(JsonschemaValidationError) as exc_info:
validate_json(instance, schema)

# Ensure that the exception has a non-empty list of validation errors
errs = exc_info.value.errors
assert type(errs) is list, "Expected a list"
assert len(errs) > 0, "Expected at least one error"
assert all(
isinstance(err, ValidationError) for err in errs
), "All errors must be `jsonschema.exceptions.ValidationError`"

0 comments on commit d5169cf

Please sign in to comment.