scrub / tests /test_entity_scorecard.py
solidprivacy-nl
Add WP23 entity scorecard tests
7e45b40
Raw
History Blame Contribute Delete
6.42 kB
import importlib.util
import json
import sys
from pathlib import Path
def load_scorecard_module():
module_path = Path(__file__).resolve().parents[1] / "benchmark" / "build_entity_scorecard.py"
spec = importlib.util.spec_from_file_location("build_entity_scorecard", module_path)
module = importlib.util.module_from_spec(spec)
assert spec.loader is not None
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
def sample_wp22_report():
return {
"schema_version": "wp22_recall_precision_runner_v1",
"synthetic_only": True,
"thresholds_applied": False,
"gold_files": ["benchmark/gold/examples/mini.gold.example.json"],
"prediction_file": "predictions.json",
"warnings": ["example warning"],
"summary": {
"gold_count": 2,
"prediction_count": 3,
"true_positive_exact": 1,
"true_positive_normalized": 2,
"false_negative_exact": 1,
"false_negative_normalized": 0,
"false_positive_exact": 2,
"false_positive_normalized": 1,
"recall_exact": 0.5,
"recall_normalized": 1.0,
"precision_exact": 0.333333,
"precision_normalized": 0.666667,
},
"by_domain": {
"legal": {
"gold_count": 2,
"prediction_count": 3,
"true_positive_exact": 1,
"true_positive_normalized": 2,
"false_negative_exact": 1,
"false_negative_normalized": 0,
"false_positive_exact": 2,
"false_positive_normalized": 1,
"recall_exact": 0.5,
"recall_normalized": 1.0,
"precision_exact": 0.333333,
"precision_normalized": 0.666667,
}
},
"by_entity_class": {
"PERSON": {
"gold_count": 1,
"prediction_count": 1,
"true_positive_exact": 1,
"true_positive_normalized": 1,
"false_negative_exact": 0,
"false_negative_normalized": 0,
"false_positive_exact": 0,
"false_positive_normalized": 0,
"recall_exact": 1.0,
"recall_normalized": 1.0,
"precision_exact": 1.0,
"precision_normalized": 1.0,
},
"PHONE": {
"gold_count": 1,
"prediction_count": 2,
"true_positive_exact": 0,
"true_positive_normalized": 1,
"false_negative_exact": 1,
"false_negative_normalized": 0,
"false_positive_exact": 2,
"false_positive_normalized": 1,
"recall_exact": 0.0,
"recall_normalized": 1.0,
"precision_exact": 0.0,
"precision_normalized": 0.5,
},
},
"false_negatives": [],
"false_positives": [{"document_id": "mini", "text": "extra"}],
"preserve_term_failures": [{"term": "cliënt"}],
"known_trap_failures": [{"trap_text": "13-03-2026"}],
"partial_overlaps": [{"policy": "diagnostic_only"}],
}
def test_build_scorecard_contains_required_report_only_fields():
scorecard_module = load_scorecard_module()
scorecard = scorecard_module.build_scorecard(sample_wp22_report(), generated_at="2026-06-12T10:00:00+00:00")
assert scorecard["schema_version"] == "wp23_entity_class_scorecard_v1"
assert scorecard["synthetic_only"] is True
assert scorecard["report_only"] is True
assert scorecard["thresholds_applied"] is False
assert scorecard["production_gate"] is False
assert scorecard["safe_for_production_claim"] is False
assert scorecard["policy"]["ci_may_publish_report"] is True
assert scorecard["policy"]["ci_may_fail_on_technical_errors"] is True
assert scorecard["policy"]["ci_must_not_fail_on_scores_yet"] is True
assert scorecard["overall"]["gold_count"] == 2
assert scorecard["overall"]["prediction_count"] == 3
assert scorecard["overall"]["true_positive_exact"] == 1
assert scorecard["overall"]["true_positive_normalized"] == 2
assert scorecard["overall"]["false_negative_count"] == 0
assert scorecard["overall"]["false_positive_count"] == 1
assert scorecard["failure_counts"]["preserve_term_failure_count"] == 1
assert scorecard["failure_counts"]["known_trap_failure_count"] == 1
assert scorecard["failure_counts"]["partial_overlap_diagnostic_count"] == 1
def test_scorecard_includes_zero_rows_for_all_entity_classes():
scorecard_module = load_scorecard_module()
scorecard = scorecard_module.build_scorecard(sample_wp22_report(), generated_at="2026-06-12T10:00:00+00:00")
by_entity = {row["entity_class"]: row for row in scorecard["per_entity_class"]}
assert "PERSON" in by_entity
assert "MEDICAL_OR_CARE_REFERENCE" in by_entity
assert by_entity["MEDICAL_OR_CARE_REFERENCE"]["gold_count"] == 0
assert by_entity["MEDICAL_OR_CARE_REFERENCE"]["prediction_count"] == 0
def test_markdown_scorecard_contains_policy_and_metrics():
scorecard_module = load_scorecard_module()
scorecard = scorecard_module.build_scorecard(sample_wp22_report(), generated_at="2026-06-12T10:00:00+00:00")
markdown = scorecard_module.render_markdown(scorecard)
assert "Report-only" in markdown
assert "No production-blocking threshold is applied" in markdown
assert "No `safe for production` claim is made" in markdown
assert "`PERSON`" in markdown
assert "`preserve_term_failure_count` | 1" in markdown
assert "`known_trap_failure_count` | 1" in markdown
assert "`partial_overlap_diagnostic_count` | 1" in markdown
def test_write_scorecard_outputs_json_and_markdown(tmp_path):
scorecard_module = load_scorecard_module()
scorecard = scorecard_module.build_scorecard(sample_wp22_report(), generated_at="2026-06-12T10:00:00+00:00")
json_path, md_path = scorecard_module.write_scorecard(scorecard, tmp_path / "reports")
parsed = json.loads(json_path.read_text(encoding="utf-8"))
assert parsed["schema_version"] == "wp23_entity_class_scorecard_v1"
assert md_path.read_text(encoding="utf-8").startswith("# SolidPrivacy Scrub — Entity-class scorecard")