packetcourt / tests /test_audit.py
DIV-45's picture
test: cover uninvoked router status
35c3c99 verified
Raw
History Blame Contribute Delete
8.03 kB
from packetcourt import audit_packet
from packetcourt.models import Verdict
from packetcourt.ocr import merge_extractions
def by_claim(result, name):
return next(claim for claim in result.claims if claim.claim == name)
def test_no_added_sugar_is_contradicted_by_glucose_syrup():
result = audit_packet(
"NO ADDED SUGAR",
"Ingredients: rolled oats, glucose syrup, peanuts. Nutrition per 100g: Total Sugar 18g.",
)
assert by_claim(result, "No Added Sugar").verdict == Verdict.CONTRADICTED
def test_multigrain_exposes_refined_flour_context():
result = audit_packet(
"MULTIGRAIN",
"Ingredients: refined wheat flour, oats, ragi flour, salt.",
)
assert by_claim(result, "Multigrain").verdict == Verdict.CONTEXT_MISSING
def test_absolute_natural_claim_is_refused():
result = audit_packet("100% NATURAL", "Ingredients: chickpea flour, salt.")
assert by_claim(result, "100% Natural").verdict == Verdict.CANNOT_VERIFY
def test_relative_best_before_date_is_resolved():
result = audit_packet(
"HIGH PROTEIN",
"PKD: 13 JUN 26. Best before 6 months from packaging. Nutrition per 100g: Protein 12g.",
)
assert result.expiry.best_before == "2026-12-13"
def test_fssai_claim_is_not_treated_as_health_endorsement():
result = audit_packet(
"FSSAI APPROVED",
"FSSAI Lic. No. 12345678901234. Ingredients: oats.",
)
audit = by_claim(result, "FSSAI Approved")
assert audit.verdict == Verdict.CONTEXT_MISSING
assert "not a health endorsement" in audit.summary
def test_whole_packet_nutrition_exposes_persuasion_gap():
result = audit_packet(
"HIGH PROTEIN",
(
"Ingredients: oats, sugar. Nutrition information per 100g: Protein 12g, "
"Total Sugars 22g, Sodium 410mg. Net weight 300g."
),
)
assert result.whole_packet.total_sugar_g == 66
assert result.whole_packet.sugar_teaspoons == 16.5
assert result.whole_packet.sodium_mg == 1230
assert any("sugar" in finding.headline.lower() for finding in result.persuasion_gap)
assert any("sodium" in finding.headline.lower() for finding in result.persuasion_gap)
def test_baked_and_whole_grain_claims_keep_material_context():
result = audit_packet(
"BAKED NOT FRIED | WHOLE GRAIN | ZERO TRANS FAT",
(
"Ingredients: refined wheat flour, whole wheat flour, oil, salt. "
"Nutrition per 100g: Sodium 780mg, Trans Fat 0g. Net weight 180g."
),
)
assert by_claim(result, "Baked Not Fried").verdict == Verdict.CONTEXT_MISSING
assert by_claim(result, "Whole Grain").verdict == Verdict.CONTEXT_MISSING
assert by_claim(result, "Zero Trans Fat").verdict == Verdict.SUPPORTED
assert any("refined" in finding.quiet_context.lower() for finding in result.persuasion_gap)
def test_after_opening_instruction_is_extracted():
result = audit_packet(
"NO PRESERVATIVES",
"Ingredients: tomato, salt. Use by: 08 JUL 2026. Consume within 3 days after opening.",
)
assert result.expiry.after_opening_instruction == "Consume within 3 days after opening"
def test_investigation_requests_missing_evidence_and_stops_explicitly():
result = audit_packet("HIGH PROTEIN", "Protein 9g.")
assert any(step.tool == "inspect_nutrition" for step in result.investigation.steps)
assert any("nutrition panel" in item.lower() for item in result.investigation.missing_evidence)
assert "missing-evidence" in result.investigation.stop_reason
assert result.agent_review.status == "NOT_REQUESTED"
def test_sugar_free_packet_surfaces_sweetener_and_requests_nutrition_panel():
result = audit_packet(
"Sugar Free\nEnriched with Extra Calcium with DHA\nReal Badam",
(
"Ingredients: Maltodextrin (65%), Badam, Soya Protein Isolate, Sucralose, "
"Vitamins and Mineral Mix. Net Weight: 200g. Dates: FEB 2024 - JUL 2025."
),
)
assert by_claim(result, "Sugar Free").verdict == Verdict.CANNOT_VERIFY
assert any("Sucralose" in evidence.text for evidence in by_claim(result, "Sugar Free").evidence)
assert any("sugar free" in finding.headline.lower() for finding in result.persuasion_gap)
assert result.ingredients[-1] == "Vitamins and Mineral Mix"
assert result.expiry.visible_date_texts == ["FEB 2024", "JUL 2025"]
assert "visible date evidence" in result.expiry.status.lower()
assert not any("visible dates" in item.lower() for item in result.investigation.missing_evidence)
sugar_step = next(step for step in result.investigation.steps if "Sugar Free" in step.reason)
assert sugar_step.tool == "inspect_nutrition"
def test_dynamic_front_claim_is_audited_instead_of_dropped():
result = audit_packet("Real Badam", "Ingredients: Maltodextrin, Badam. Net Weight: 200g.")
claim = by_claim(result, "Real Badam")
assert claim.verdict == Verdict.CONTEXT_MISSING
assert any("Badam" in evidence.text for evidence in claim.evidence)
def test_multiple_photo_extractions_are_labeled_and_exact_duplicates_are_skipped():
text, status, images = merge_extractions(
[
("HIGH PROTEIN", "read one"),
("HIGH PROTEIN", "read duplicate"),
("REAL BADAM", "read another panel"),
],
"front",
)
assert "[Front photo 1]" in text
assert "[Front photo 3]" in text
assert text.count("HIGH PROTEIN") == 1
assert "2 unique front photos" in status
assert "Exact duplicate skipped" in images[1]["status"]
def test_table_style_nutrition_values_are_calculated():
result = audit_packet(
"SUGAR FREE",
(
"Nutrition Information Per 100g | Protein (g) 12 | Total Sugars | g | 0 | "
"Added Sugar (g) 0 | Sodium (mg) 410 | Saturated Fat g 2.5 | Net Weight: 200g."
),
)
assert result.nutrition.protein_g == 12
assert result.nutrition.total_sugar_g == 0
assert result.nutrition.sodium_mg == 410
assert result.whole_packet.protein_g == 24
assert result.whole_packet.total_sugar_g == 0
assert result.whole_packet.sodium_mg == 820
def test_basis_and_packet_without_nutrient_rows_explain_the_real_missing_evidence():
result = audit_packet("SUGAR FREE", "Nutrition Information Per 100g. Net Weight: 200g.")
assert result.whole_packet.calculable is False
assert "no readable nutrient quantities" in result.whole_packet.explanation.lower()
assert any("nutrient quantities" in item.lower() for item in result.investigation.missing_evidence)
def test_enrichment_claim_cites_visible_back_label_evidence():
result = audit_packet(
"Extra Calcium with DHA",
"Nutrition per 100g: Calcium 400mg, DHA 25mg. Net Weight: 200g.",
)
claim = by_claim(result, "Extra Calcium with DHA")
assert claim.verdict == Verdict.CONTEXT_MISSING
assert any("Calcium" in evidence.text for evidence in claim.evidence)
def test_ambiguous_dates_are_supplementary_when_not_a_front_claim():
result = audit_packet("SUGAR FREE", "Nutrition per 100g: Total Sugars 0g. Net Weight 200g. FEB 2024 JUL 2025.")
assert result.expiry.visible_date_texts == ["FEB 2024", "JUL 2025"]
assert not any("date" in item.lower() for item in result.investigation.missing_evidence)
def test_one_character_ocr_claim_mismatch_is_surfaced_conservatively():
result = audit_packet("Real Badar", "Ingredients: ** Maltodextrin (65%), Badam, Sucralose.")
claim = by_claim(result, "Real Badar")
assert claim.verdict == Verdict.CANNOT_VERIFY
assert claim.confidence == "low"
assert any(evidence.text == "Badam" for evidence in claim.evidence)
assert result.ingredients[0] == "Maltodextrin (65%)"
def test_no_claims_reports_router_not_invoked_instead_of_fallback():
result = audit_packet("PRODUCT NAME", "Ingredients: oats.")
assert result.claims == []
assert result.investigation.router_model == "not invoked: no auditable front claims detected"