"""The finished-import checklist must fail loudly on every verified failure mode.""" from __future__ import annotations import pytest from mealie_mcp.verify import is_parsed, verify_recipe def complete_recipe(**overrides): """A recipe that satisfies every check, so each test can break exactly one thing.""" recipe = { "name": "Kycklinggryta med kokos", "slug": "kycklinggryta-med-kokos", "description": "Krämig gryta med kokosmjölk och röd curry.", "recipeInstructions": [{"text": "Bryn kycklingen."}, {"text": "Häll i kokosmjölken."}], "recipeIngredient": [ { "display": "400 g kycklinglårfilé", "quantity": 400, "unit": {"id": "u1", "name": "g"}, "food": {"id": "f1", "name": "kycklinglårfilé"}, }, { "display": "1 burk kokosmjölk", "quantity": 1, "unit": {"id": "u2", "name": "burk"}, "food": {"id": "f2", "name": "kokosmjölk"}, }, ], "image": "abc123", "recipeCategory": [{"name": "Huvudrätter"}], "tags": [{"name": "Källa: Köket"}], "orgURL": "https://www.koket.se/kycklinggryta", } recipe.update(overrides) return recipe def status_of(report, check): return next(c["status"] for c in report["checks"] if c["check"] == check) class TestCompleteRecipe: def test_fully_finished_import_passes(self): report = verify_recipe(complete_recipe(), image_verified=True) assert report["complete"] is True assert report["failed"] == [] class TestCoverImage: def test_image_field_alone_is_only_a_warning_not_a_pass(self): # Verified: a non-empty image field does not prove the UI shows a cover. report = verify_recipe(complete_recipe(), image_verified=False) assert status_of(report, "cover_image") == "warn" def test_missing_image_fails(self): report = verify_recipe(complete_recipe(image=None)) assert status_of(report, "cover_image") == "fail" assert report["complete"] is False class TestSwedishContent: def test_english_ingredient_line_fails(self): recipe = complete_recipe() recipe["recipeIngredient"][0]["display"] = "2 cups flour" report = verify_recipe(recipe, image_verified=True) assert status_of(report, "swedish_ingredients") == "fail" assert report["complete"] is False def test_english_instructions_fail(self): recipe = complete_recipe(recipeInstructions=[{"text": "Add 2 tbsp of butter"}]) report = verify_recipe(recipe, image_verified=True) assert status_of(report, "swedish_prose") == "fail" def test_swedish_title_only_is_not_enough(self): # Rule: translation is not done until the ingredient lines themselves are Swedish. recipe = complete_recipe() recipe["recipeIngredient"][1]["display"] = "1 clove garlic" report = verify_recipe(recipe, image_verified=True) assert report["complete"] is False class TestParseState: def test_unstructured_ingredients_leave_recipe_unparsed(self): recipe = complete_recipe( recipeIngredient=[{"display": "400 g kycklinglårfilé"}, {"display": "1 burk kokosmjölk"}] ) assert is_parsed(recipe) is False report = verify_recipe(recipe, image_verified=True) assert status_of(report, "ingredients_parsed") == "fail" def test_empty_recipe_is_not_parsed(self): assert is_parsed({"recipeIngredient": []}) is False def test_structured_ingredients_are_parsed(self): assert is_parsed(complete_recipe()) is True class TestReadability: def test_parser_mangled_display_fails_even_when_structured(self): # Structure is worthless if the human-facing line is garbage. recipe = complete_recipe() recipe["recipeIngredient"][0]["display"] = "47⁄10 liter basmatiris" report = verify_recipe(recipe, image_verified=True) assert status_of(report, "ingredient_lines_readable") == "fail" class TestExtractionPlaceholders: def test_could_not_detect_placeholder_fails(self): recipe = complete_recipe() recipe["recipeIngredient"][0]["display"] = "Could not detect ingredients" report = verify_recipe(recipe, image_verified=True) assert status_of(report, "no_extraction_placeholders") == "fail" class TestTaxonomy: def test_untagged_recipe_fails_without_a_reason(self): report = verify_recipe( complete_recipe(recipeCategory=[], tags=[]), image_verified=True ) assert status_of(report, "taxonomy") == "fail" def test_deliberate_skip_passes(self): report = verify_recipe( complete_recipe(recipeCategory=[], tags=[]), image_verified=True, taxonomy_skipped_reason="Fredrik sorterar den manuellt", ) assert status_of(report, "taxonomy") == "pass" assert report["complete"] is True class TestAttribution: def test_missing_source_warns_but_does_not_block(self): report = verify_recipe( complete_recipe(orgURL=None, extras={}), image_verified=True ) assert status_of(report, "attribution") == "warn" assert report["complete"] is True