feat: rapidfuzz-based ingredient<->grocery matcher with manual-pin preservation

This commit is contained in:
2026-05-06 06:18:18 -07:00
parent 489ee03574
commit 6dfb84310f
2 changed files with 179 additions and 0 deletions
+44
View File
@@ -0,0 +1,44 @@
import pytest
from app.services.matcher import build_match_pool, rank_candidates
def test_build_match_pool_includes_aliases() -> None:
ingredients = [
{"id": "i1", "name": "Chicken Thighs", "aliases": ["chicken thigh"]},
{"id": "i2", "name": "Chicken Breast", "aliases": []},
]
pool = build_match_pool(ingredients)
# 1 name + 1 alias for i1, 1 name for i2 = 3 entries
assert len(pool) == 3
assert ("Chicken Thighs", "i1") in pool
assert ("chicken thigh", "i1") in pool
def test_rank_candidates_top_n_with_threshold() -> None:
pool = [
("Chicken Thighs", "i1"),
("chicken thigh", "i1"),
("Chicken Breast", "i2"),
("Pork Chops", "i3"),
]
ranked = rank_candidates(
target="Foster Farms Chicken Thighs Family Pack",
pool=pool,
top_n=3,
threshold=0.75,
)
ids = [item["ingredient_id"] for item in ranked]
assert ids[0] == "i1"
assert all(item["confidence"] >= 0.75 for item in ranked)
def test_rank_candidates_drops_below_threshold() -> None:
pool = [("Pork Chops", "i3")]
ranked = rank_candidates(
target="Frosted Flakes Cereal 18oz",
pool=pool,
top_n=3,
threshold=0.75,
)
assert ranked == []