diff --git a/mealie_mcp/verify.py b/mealie_mcp/verify.py index 527e702..f62ee62 100644 --- a/mealie_mcp/verify.py +++ b/mealie_mcp/verify.py @@ -16,6 +16,10 @@ from .normalize import ( looks_mangled, ) +#: Mealie stores this sentinel in ``image`` when a recipe has no cover, so a +#: truthiness test on the field reports a cover that is not there. +NO_IMAGE_SENTINEL = "no image" + Status = Literal["pass", "fail", "warn"] @@ -23,6 +27,19 @@ def _check(check_id: str, status: Status, detail: str) -> dict[str, str]: return {"check": check_id, "status": status, "detail": detail} +def has_cover_image(recipe: dict[str, Any]) -> bool: + """Whether the recipe carries a real cover image. + + Mealie writes the literal string ``"no image"`` instead of an empty value + when a recipe has no cover, so an emptiness test has to reject that + sentinel too. + """ + image = recipe.get("image") + if not isinstance(image, str): + return bool(image) + return image.strip().casefold() not in ("", NO_IMAGE_SENTINEL) + + def is_parsed(recipe: dict[str, Any]) -> bool: """Whether Mealie will stop showing 'your ingredients aren't parsed yet'. @@ -149,7 +166,7 @@ def verify_recipe( ) ) - has_image_field = bool(recipe.get("image")) + has_image_field = has_cover_image(recipe) if image_verified: image_status: Status = "pass" image_detail = "Image step ran and was verified by the caller" diff --git a/tests/test_verify.py b/tests/test_verify.py index 2e4cb8f..13a2763 100644 --- a/tests/test_verify.py +++ b/tests/test_verify.py @@ -61,6 +61,14 @@ class TestCoverImage: assert status_of(report, "cover_image") == "fail" assert report["complete"] is False + @pytest.mark.parametrize("stored", ["no image", "No Image", " no image ", ""]) + def test_mealies_no_image_sentinel_fails(self, stored): + # Verified against Mealie 3.22: a recipe without a cover stores the + # string "no image", which a truthiness test reads as a cover present. + report = verify_recipe(complete_recipe(image=stored)) + assert status_of(report, "cover_image") == "fail" + assert report["complete"] is False + class TestSwedishContent: def test_english_ingredient_line_fails(self):