""" Validate pipeline/prefilter.py against the 50-post oracle. Key invariant: zero false negatives — no true food post may be skipped. Reports non-food recall (how many of the 20 non-food posts are correctly caught). Run: python3 tests/test_prefilter.py """ import json import sys from pathlib import Path # Allow running from repo root without installing the package sys.path.insert(0, str(Path(__file__).parent.parent)) from pipeline.prefilter import should_skip FIXTURE = Path(__file__).parent / "fixtures" / "test_posts.json" def main(): posts = json.loads(FIXTURE.read_text()) place_posts = [p for p in posts if p.get("ground_truth", {}).get("is_place")] non_place_posts = [p for p in posts if not p.get("ground_truth", {}).get("is_place")] # False negatives: place posts incorrectly skipped — must be zero false_negatives = [p for p in place_posts if should_skip(p)] # True positives: non-place posts correctly caught true_positives = [p for p in non_place_posts if should_skip(p)] print(f"Oracle: {len(place_posts)} place, {len(non_place_posts)} non-place\n") if false_negatives: print(f"FAIL — {len(false_negatives)} place post(s) incorrectly pre-filtered:") for p in false_negatives: cap = (p.get("caption") or "")[:120] print(f" • {cap!r}") print() else: print(f"PASS — zero false negatives (no place post skipped)\n") pct = len(true_positives) / len(non_place_posts) * 100 if non_place_posts else 0 print(f"Non-place recall: {len(true_positives)}/{len(non_place_posts)} correctly pre-filtered ({pct:.0f}%)") for p in true_positives: cap = (p.get("caption") or "")[:100] print(f" ✓ {cap!r}") missed = [p for p in non_place_posts if not should_skip(p)] if missed: print(f"\nNon-place posts NOT caught by pre-filter ({len(missed)}) — will reach LLM:") for p in missed: cap = (p.get("caption") or "")[:100] print(f" · {cap!r}") sys.exit(1 if false_negatives else 0) if __name__ == "__main__": main()