Venket / tests /test_validation_engine.py
venki101's picture
Add files using upload-large-folder tool
3f820d9 verified
Raw History Blame Contribute Delete
4.25 kB
"""Unit tests for src.analysis.validation_engine."""
from __future__ import annotations
import copy
from src.analysis.validation_engine import ValidationEngine, summarize
from src.config.schema import DifficultyWeights, ValidationConfig
VALID_METADATA = {
"document_id": "sample_000001",
"template": "bank_doc_01",
"page": 1,
"page_size": [1000, 800],
"signer": {"name": "Jane Doe", "role": "Account Holder"},
"signature_source": {"signature_id": "sig_0001", "path": "dataset/genuine/x.png"},
"placement": {"rotation_deg": 0.0},
"rendering": {"blend_mode": "multiply"},
"difficulty": {
"misplacement": 0.6, "whitespace": 0.5, "faintness": 0.5, "smallness": 0.5, "low_texture": 0.5,
"score": round(0.35 * 0.6 + 0.20 * 0.5 + 0.20 * 0.5 + 0.15 * 0.5 + 0.10 * 0.5, 4),
"tier": "medium",
},
"quality": {"degraded": False},
"verification": {
"case_type": None, "verdict": None, "reject_code": None,
"target_box_id": None, "expected_signer_group": None, "expected_signer_script": None,
"actual_signer_group": None, "actual_source_type": None, "actual_signer_script": None,
"companion": None,
},
"augmentation": {},
"geometry": {
"detected": True,
"ink_pixels": 120,
"target_box": [100, 100, 200, 150],
"actual_box": [110, 105, 190, 145],
"signature_bbox_px": [110, 105, 190, 145],
"box_px": [100, 100, 200, 150],
"signature_polygon_px": [[110, 105], [190, 105], [190, 145], [110, 145]],
},
"metrics": {"box_coverage_pct": 80.0},
}
def _engine(strict: bool = False) -> ValidationEngine:
return ValidationEngine(ValidationConfig(strict=strict), DifficultyWeights())
def test_valid_metadata_passes():
report = _engine().validate(copy.deepcopy(VALID_METADATA))
assert report.passed, [i.message for i in report.issues]
assert not report.errors
def test_missing_top_level_key_fails():
bad = copy.deepcopy(VALID_METADATA)
del bad["geometry"]
report = _engine().validate(bad)
assert not report.passed
assert any("geometry" in i.field for i in report.errors)
def test_signature_not_detected_fails():
bad = copy.deepcopy(VALID_METADATA)
bad["geometry"]["detected"] = False
report = _engine().validate(bad)
assert not report.passed
def test_degenerate_polygon_fails():
bad = copy.deepcopy(VALID_METADATA)
bad["geometry"]["signature_polygon_px"] = [[0, 0], [1, 1]]
report = _engine().validate(bad)
assert not report.passed
def test_invalid_box_ordering_fails():
bad = copy.deepcopy(VALID_METADATA)
bad["geometry"]["target_box"] = [200, 100, 100, 150] # x2 < x1
report = _engine().validate(bad)
assert not report.passed
def test_box_outside_page_is_warning_not_error_by_default():
bad = copy.deepcopy(VALID_METADATA)
bad["geometry"]["target_box"] = [900, 700, 1200, 900] # exceeds page_size [1000, 800]
report = _engine(strict=False).validate(bad)
assert report.passed # warning only, not strict
assert report.warnings
report_strict = _engine(strict=True).validate(bad)
assert not report_strict.passed
def test_nan_value_fails():
bad = copy.deepcopy(VALID_METADATA)
bad["metrics"]["box_coverage_pct"] = float("nan")
report = _engine().validate(bad)
assert not report.passed
def test_difficulty_score_mismatch_fails():
bad = copy.deepcopy(VALID_METADATA)
bad["difficulty"]["score"] = 0.999 # inconsistent with components
report = _engine().validate(bad)
assert not report.passed
def test_difficulty_tier_mismatch_fails():
bad = copy.deepcopy(VALID_METADATA)
bad["difficulty"]["tier"] = "hard" # inconsistent with score ~0.49 -> medium
report = _engine().validate(bad)
assert not report.passed
def test_summarize_batch():
good = _engine().validate(copy.deepcopy(VALID_METADATA))
bad_meta = copy.deepcopy(VALID_METADATA)
bad_meta["geometry"]["detected"] = False
bad = _engine().validate(bad_meta)
summary = summarize([good, bad])
assert summary.total == 2
assert summary.passed == 1
assert summary.failed == 1
assert "sample_000001" in summary.failed_sample_ids