Public Access
feat: run matcher after successful scrape; failures don't flip scrape status
This commit is contained in:
@@ -86,6 +86,12 @@ def _run_scrape_in_background(
|
||||
log.duration_seconds = int(
|
||||
(log.completed_at - log.started_at).total_seconds()
|
||||
)
|
||||
try:
|
||||
from app.services.matcher import run_match_job
|
||||
run_match_job(db, source_filter="lucky_california")
|
||||
except Exception as e: # matcher failure must not flip scrape to FAILED
|
||||
import logging
|
||||
logging.exception("matcher failed after successful scrape: %s", e)
|
||||
db.commit()
|
||||
logger.info(
|
||||
"Background scrape %s complete: %s/%s items saved",
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import pytest
|
||||
|
||||
pytestmark = pytest.mark.requires_postgres
|
||||
|
||||
|
||||
def test_run_match_job_persists_top_matches(monkeypatch):
|
||||
"""End-to-end: seed Ingredient + GroceryItem rows, run the matcher,
|
||||
verify ingredient_grocery_match rows exist with confidence >= 0.75.
|
||||
"""
|
||||
from datetime import datetime, timezone
|
||||
from decimal import Decimal
|
||||
from uuid import uuid4
|
||||
|
||||
from app.database import SessionLocal
|
||||
from app.models import (
|
||||
GroceryItem,
|
||||
Ingredient,
|
||||
IngredientGroceryMatch,
|
||||
IngredientMatchSource,
|
||||
)
|
||||
from app.services.matcher import run_match_job
|
||||
|
||||
setup = SessionLocal()
|
||||
ing_id = uuid4()
|
||||
grocery_id = uuid4()
|
||||
try:
|
||||
setup.add(
|
||||
Ingredient(
|
||||
id=ing_id,
|
||||
name="Zorblax Fizzberry Test",
|
||||
name_lower="zorblax fizzberry test",
|
||||
aliases=["zorblax fizzberry"],
|
||||
aisle="meat",
|
||||
unit="lb",
|
||||
)
|
||||
)
|
||||
setup.add(
|
||||
GroceryItem(
|
||||
id=grocery_id,
|
||||
name="Zorblax Fizzberry Family Pack",
|
||||
source="lucky_california",
|
||||
external_id="ext-test-1",
|
||||
current_price=Decimal("3.99"),
|
||||
regular_price=Decimal("5.49"),
|
||||
is_on_sale=True,
|
||||
scraped_at=datetime.now(timezone.utc),
|
||||
)
|
||||
)
|
||||
setup.commit()
|
||||
finally:
|
||||
setup.close()
|
||||
|
||||
work = SessionLocal()
|
||||
try:
|
||||
written = run_match_job(work)
|
||||
assert written >= 1
|
||||
rows = (
|
||||
work.query(IngredientGroceryMatch)
|
||||
.filter(IngredientGroceryMatch.ingredient_id == ing_id)
|
||||
.all()
|
||||
)
|
||||
assert any(
|
||||
r.grocery_item_id == grocery_id
|
||||
and r.confidence >= Decimal("0.750")
|
||||
and r.source == IngredientMatchSource.AUTO
|
||||
for r in rows
|
||||
)
|
||||
finally:
|
||||
cleanup = SessionLocal()
|
||||
try:
|
||||
cleanup.query(IngredientGroceryMatch).filter(
|
||||
IngredientGroceryMatch.ingredient_id == ing_id
|
||||
).delete()
|
||||
cleanup.query(GroceryItem).filter(GroceryItem.id == grocery_id).delete()
|
||||
cleanup.query(Ingredient).filter(Ingredient.id == ing_id).delete()
|
||||
cleanup.commit()
|
||||
finally:
|
||||
cleanup.close()
|
||||
work.close()
|
||||
Reference in New Issue
Block a user