from __future__ import annotations import json from pathlib import Path import pytest from bl_ocr.domain.schema_validation import ( SchemaError, build_validator, schema_errors, ) PROJECT_ROOT = Path(__file__).resolve().parents[1] def _schema() -> dict: return json.loads((PROJECT_ROOT / "schema.json").read_text(encoding="utf-8")) def _example() -> dict: return json.loads((PROJECT_ROOT / "example.json").read_text(encoding="utf-8")) def test_valid_example_has_no_errors(): validator = build_validator(_schema()) assert schema_errors(_example(), validator) == [] def test_extra_key_is_rejected(): annotation = _example() annotation["products"][0]["_repaired"] = "note" # provenance must never appear inside errors = schema_errors(annotation, _schema()) assert any(e.validator == "additionalProperties" for e in errors) def test_quantity_above_bound_is_rejected(): annotation = _example() annotation["products"][0]["quantity"]["value"] = 2000 errors = schema_errors(annotation, _schema()) assert any(e.validator == "maximum" for e in errors) def test_null_value_is_accepted(): annotation = _example() annotation["products"][0]["expiry_date"]["value"] = None assert schema_errors(annotation, _schema()) == [] def test_missing_required_product_field_is_rejected(): annotation = _example() del annotation["products"][0]["pharmacist_price_ttc"] errors = schema_errors(annotation, _schema()) assert any(e.validator == "required" for e in errors) def test_errors_are_structured_and_limited(): annotation = _example() annotation["products"][0]["quantity"]["value"] = "not-a-number" errors = schema_errors(annotation, _schema(), limit=5) assert len(errors) <= 5 assert all({"path", "message", "validator"} == set(e.as_dict()) for e in errors) def test_invalid_schema_raises(): with pytest.raises(SchemaError): build_validator({"type": "not-a-real-type"})