diff --git a/app/services/recipe/staple_library.py b/app/services/recipe/staple_library.py index 46474e9..6ece0bc 100644 --- a/app/services/recipe/staple_library.py +++ b/app/services/recipe/staple_library.py @@ -6,6 +6,7 @@ from __future__ import annotations from dataclasses import dataclass from pathlib import Path +from typing import Any import yaml @@ -21,7 +22,7 @@ class StapleEntry: base_ingredients: list[str] base_method: str base_time_minutes: int - yield_formats: dict[str, dict] + yield_formats: dict[str, Any] compatible_styles: list[str] @@ -42,15 +43,18 @@ class StapleLibrary: return [s for s in self._staples.values() if label in s.dietary_labels] def _load(self, path: Path) -> StapleEntry: - data = yaml.safe_load(path.read_text()) - return StapleEntry( - slug=data["slug"], - name=data["name"], - description=data.get("description", ""), - dietary_labels=data.get("dietary_labels", []), - base_ingredients=data.get("base_ingredients", []), - base_method=data.get("base_method", ""), - base_time_minutes=int(data.get("base_time_minutes", 0)), - yield_formats=data.get("yield_formats", {}), - compatible_styles=data.get("compatible_styles", []), - ) + try: + data = yaml.safe_load(path.read_text()) + return StapleEntry( + slug=data["slug"], + name=data["name"], + description=data.get("description", ""), + dietary_labels=data.get("dietary_labels", []), + base_ingredients=data.get("base_ingredients", []), + base_method=data.get("base_method", ""), + base_time_minutes=int(data.get("base_time_minutes", 0)), + yield_formats=data.get("yield_formats", {}), + compatible_styles=data.get("compatible_styles", []), + ) + except (KeyError, yaml.YAMLError) as exc: + raise ValueError(f"Failed to load staple from {path}: {exc}") from exc diff --git a/app/staples/tofu_firm.yaml b/app/staples/tofu_firm.yaml index dbb4034..38d0a33 100644 --- a/app/staples/tofu_firm.yaml +++ b/app/staples/tofu_firm.yaml @@ -1,4 +1,4 @@ -slug: tofu-firm +slug: tofu_firm name: Firm Tofu description: Pressed soybean curd. Neutral flavor, excellent at absorbing surrounding flavors. Freeze-thaw cycle creates meatier texture. dietary_labels: [vegan, high-protein] diff --git a/tests/services/recipe/test_staple_library.py b/tests/services/recipe/test_staple_library.py index 777d5e8..d48e19e 100644 --- a/tests/services/recipe/test_staple_library.py +++ b/tests/services/recipe/test_staple_library.py @@ -22,3 +22,27 @@ def test_list_all_staples(): slugs = [s.slug for s in all_staples] assert "seitan" in slugs assert "tempeh" in slugs + + +def test_tofu_firm_is_loadable(): + from app.services.recipe.staple_library import StapleLibrary + lib = StapleLibrary() + tofu = lib.get("tofu_firm") + assert tofu is not None + assert tofu.slug == "tofu_firm" + + +def test_filter_by_dietary_vegan(): + from app.services.recipe.staple_library import StapleLibrary + lib = StapleLibrary() + vegan = lib.filter_by_dietary("vegan") + assert len(vegan) > 0 + assert all("vegan" in s.dietary_labels for s in vegan) + + +def test_list_all_returns_all_three(): + from app.services.recipe.staple_library import StapleLibrary + lib = StapleLibrary() + all_staples = lib.list_all() + slugs = {s.slug for s in all_staples} + assert {"seitan", "tempeh", "tofu_firm"} == slugs