| """Unit tests for normalization/merger.py.""" |
|
|
| from __future__ import annotations |
|
|
| from confidence.engine import ConfidenceEngine |
| from confidence.conflicts import ConflictDetector |
| from models.providers import ProviderCapability |
| from normalization.merger import ReportMerger |
| from providers.base import ProviderResult |
|
|
|
|
| def _make_result(provider: str, capability: ProviderCapability, |
| normalized: dict, success: bool = True) -> ProviderResult: |
| return ProviderResult( |
| provider=provider, |
| capability=capability, |
| success=success, |
| elapsed_ms=10.0, |
| normalized=normalized, |
| ) |
|
|
|
|
| class TestReportMerger: |
| def test_merge_empty_results(self): |
| engine = ConfidenceEngine() |
| cd = ConflictDetector() |
| merger = ReportMerger(engine, cd) |
| report = merger.merge({}, "hash", "job-id", 100.0, "detection") |
| assert report.metadata.image_hash == "hash" |
| assert report.metadata.job_id == "job-id" |
| assert report.metadata.total_elapsed_ms == 100.0 |
| assert report.detections == [] |
| assert report.matches == [] |
| assert report.evidence == [] |
| assert report.conflicts == [] |
|
|
| def test_merge_detection_results(self): |
| engine = ConfidenceEngine() |
| cd = ConflictDetector() |
| merger = ReportMerger(engine, cd) |
| results = { |
| "haar": _make_result( |
| "haar", ProviderCapability.DETECTION, |
| {"boxes": [{"x": 10, "y": 20, "w": 100, "h": 120}], |
| "confidences": [0.95], "landmarks": None, "num_faces": 1}, |
| ), |
| } |
| report = merger.merge(results, "hash", "job-id", 50.0, "detection") |
| assert len(report.detections) == 1 |
| assert report.detections[0].box == {"x": 10, "y": 20, "w": 100, "h": 120} |
| assert "haar" in report.detections[0].detected_by |
| assert report.detections[0].confidence.overall > 0.0 |
| assert len(report.evidence) == 1 |
| assert report.evidence[0].provider == "haar" |
|
|
| def test_merge_preserves_failed_results_as_evidence(self): |
| engine = ConfidenceEngine() |
| cd = ConflictDetector() |
| merger = ReportMerger(engine, cd) |
| results = { |
| "dnn": ProviderResult( |
| provider="dnn", capability=ProviderCapability.DETECTION, |
| success=False, elapsed_ms=5.0, error="model not loaded", |
| error_type="RuntimeError", |
| ), |
| } |
| report = merger.merge(results, "hash", "job-id", 5.0, "detection") |
| |
| assert report.detections == [] |
| |
| assert len(report.evidence) == 1 |
| assert report.evidence[0].success is False |
| assert report.evidence[0].error == "model not loaded" |
| assert "dnn" in report.metadata.providers_failed |
|
|
| def test_merge_image_analysis_results(self): |
| engine = ConfidenceEngine() |
| cd = ConflictDetector() |
| merger = ReportMerger(engine, cd) |
| results = { |
| "image_quality": _make_result( |
| "image_quality", ProviderCapability.IMAGE_ANALYSIS, |
| {"quality_score": 0.85, "brightness": 128.0, "contrast": 50.0, |
| "sharpness": 200.0, "noise_level": 5.0, "width": 200, "height": 200, |
| "channels": 3, "color_profile": "BGR", "dominant_colors": ["#ff0000"]}, |
| ), |
| } |
| report = merger.merge(results, "hash", "job-id", 30.0, "image_analysis") |
| assert len(report.image_analyses) == 1 |
| assert report.image_analyses[0].provider == "image_quality" |
| assert report.image_analyses[0].quality_score == 0.85 |
| assert report.image_analyses[0].confidence is not None |
|
|
| def test_merge_metadata_results(self): |
| engine = ConfidenceEngine() |
| cd = ConflictDetector() |
| merger = ReportMerger(engine, cd) |
| results = { |
| "exif": _make_result( |
| "exif", ProviderCapability.METADATA, |
| {"format": "JPEG", "exif": {"Make": "Canon"}, "camera_make": "Canon"}, |
| ), |
| } |
| report = merger.merge(results, "hash", "job-id", 20.0, "metadata") |
| assert len(report.metadata_extractions) == 1 |
| assert report.metadata_extractions[0].format == "JPEG" |
| assert report.metadata_extractions[0].camera_make == "Canon" |
|
|
| def test_merge_forensics_results(self): |
| engine = ConfidenceEngine() |
| cd = ConflictDetector() |
| merger = ReportMerger(engine, cd) |
| results = { |
| "image_integrity": _make_result( |
| "image_integrity", ProviderCapability.FORENSICS, |
| {"integrity_score": 0.95, "manipulation_indicators": [], |
| "details": {"sha256": "abc123"}}, |
| ), |
| } |
| report = merger.merge(results, "hash", "job-id", 15.0, "forensics") |
| assert len(report.forensics) == 1 |
| assert report.forensics[0].integrity_score == 0.95 |
|
|
| def test_merge_collects_limitations(self): |
| engine = ConfidenceEngine() |
| cd = ConflictDetector() |
| merger = ReportMerger(engine, cd) |
| results = { |
| "dnn": ProviderResult( |
| provider="dnn", capability=ProviderCapability.DETECTION, |
| success=False, elapsed_ms=5.0, error="model not loaded", |
| ), |
| } |
| report = merger.merge(results, "hash", "job-id", 5.0, "detection") |
| assert any("dnn" in lim for lim in report.metadata.limitations) |
|
|