munch-ease-backend/tests/test_recipes_offline_expected.py

97 lines
2.9 KiB
Python
Raw Permalink Normal View History

2025-11-04 07:31:13 +00:00
import asyncio
import json
from pathlib import Path
from types import SimpleNamespace
import pytest
from db import connect, create
from recipes import parse_recipe_from_html
SNAP_DIR = Path("tests/sample_files/recipes")
def _iter_expected_files():
return sorted(SNAP_DIR.glob("*.recipe.json"))
def _load_expected(path: Path):
# New-style file contains the model JSON directly or null
return json.loads(path.read_text())
def _expected_subset(expected: dict):
# expected is serialized with by_alias=True, so image_urls is imageUrls
name = expected.get("name")
link = expected.get("link")
serves = expected.get("serves")
image_urls = expected.get("imageUrls") or []
ingredients = expected.get("ingredients") or []
ing_lines = [ing.get("line") for ing in ingredients]
return {
"name": name,
"link": link,
"serves": serves,
"image_urls": image_urls,
"ingredient_lines": ing_lines,
}
def _actual_subset(model):
# Stable, assertion-friendly projection
return {
"name": model.name,
"link": model.link,
"serves": model.serves,
"image_urls": list(model.image_urls or []),
"ingredient_lines": [ing.line for ing in (model.ingredients or [])],
}
@pytest.mark.parametrize("exp_path", _iter_expected_files())
def test_offline_parse_matches_expected(exp_path: Path):
async def _run():
2025-11-05 08:58:08 +00:00
# exp_path is <slug>.recipe.json; Path.stem removes only the last suffix (".json"),
# leaving ".recipe" in the stem. Strip the trailing ".recipe" to get the HTML slug.
2025-11-04 07:31:13 +00:00
slug = exp_path.stem
2025-11-05 08:58:08 +00:00
if slug.endswith(".recipe"):
slug = slug[: -len(".recipe")]
2025-11-04 07:31:13 +00:00
expected = _load_expected(exp_path)
html_path = SNAP_DIR / f"{slug}.html"
assert html_path.exists(), f"snapshot HTML missing for {slug}"
html = html_path.read_text()
# Discover base URL from meta if available
meta_path = SNAP_DIR / f"{slug}.meta.json"
base_url = slug
if meta_path.exists():
try:
meta = json.loads(meta_path.read_text())
base_url = meta.get("final_url") or meta.get("url") or slug
except Exception:
pass
created_by = SimpleNamespace(id=1, display_name="Snapshot Generator")
conn = await connect()
await create(conn)
try:
model = await parse_recipe_from_html(conn, created_by, base_url, html)
finally:
await conn.close()
if expected is None:
assert model is None, f"expected no parse for {slug}, but got a model"
return
assert model is not None, f"expected a model for {slug}, got None"
exp_sub = _expected_subset(expected)
act_sub = _actual_subset(model)
# Compare key fields; ingredients compare by line only for stability
assert act_sub == exp_sub
asyncio.run(_run())