from decimal import Decimal from uuid import uuid4 import pytest from app.services.planner.cost import compute_recipe_cost from app.services.planner.types import RecipeCost def _ingredient(ing_id, qty=1.0, unit=None): return {"ingredient_id": ing_id, "qty": qty, "unit": unit} def _match(grocery_id, name, current, regular, is_on_sale, confidence=0.9): return { "grocery_item_id": grocery_id, "grocery_item_name": name, "ingredient_name": name.lower(), "current_price": Decimal(str(current)), "regular_price": Decimal(str(regular)), "is_on_sale": is_on_sale, "confidence": Decimal(str(confidence)), "grocery_unit": None, } def test_compute_recipe_cost_sums_ingredient_costs(): recipe_id = uuid4() i1, i2 = uuid4(), uuid4() g1, g2 = uuid4(), uuid4() matches = { i1: [_match(g1, "Chicken", current=4.00, regular=5.00, is_on_sale=True)], i2: [_match(g2, "Olive Oil", current=8.00, regular=8.00, is_on_sale=False)], } pantry_ingredient_ids = set() result = compute_recipe_cost( recipe_id=recipe_id, ingredients=[_ingredient(i1, qty=2.0), _ingredient(i2, qty=1.0)], match_index=matches, pantry_ingredient_ids=pantry_ingredient_ids, ) assert isinstance(result, RecipeCost) assert result.recipe_id == recipe_id assert result.total_ingredient_count == 2 assert result.matched_ingredient_count == 2 assert result.sale_ingredient_count == 1 assert result.total_cost == Decimal("16.00") # 2*4 + 1*8 assert result.total_savings == Decimal("2.00") # 2*(5-4) def test_unmatched_ingredient_zero_cost_and_savings(): recipe_id = uuid4() i1 = uuid4() result = compute_recipe_cost( recipe_id=recipe_id, ingredients=[_ingredient(i1, qty=1.0)], match_index={}, pantry_ingredient_ids=set(), ) assert result.matched_ingredient_count == 0 assert result.total_cost == Decimal("0.00") assert result.total_savings == Decimal("0.00") def test_pantry_hits_counted(): recipe_id = uuid4() i1, i2 = uuid4(), uuid4() g1, g2 = uuid4(), uuid4() matches = { i1: [_match(g1, "X", current=2.00, regular=2.00, is_on_sale=False)], i2: [_match(g2, "Y", current=3.00, regular=3.00, is_on_sale=False)], } result = compute_recipe_cost( recipe_id=recipe_id, ingredients=[_ingredient(i1), _ingredient(i2)], match_index=matches, pantry_ingredient_ids={i1}, ) assert result.pantry_hit_count == 1 assert result.pantry_hit_pct == 0.5