From c33a51d80b608e09019467e541bd92baa95adb7b Mon Sep 17 00:00:00 2001 From: James Brechtel Date: Wed, 20 May 2026 15:57:14 -0400 Subject: [PATCH] =?UTF-8?q?test:=20add=20step=20count=20preservation=20tes?= =?UTF-8?q?ts=20for=20SchemaOrg=20=E2=86=92=20Cooklang=20conversion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds checkStepCount helper that verifies the number of instructions in the parsed schema.org recipe matches the number of SecStep items in the Method section of the resulting Cooklang Recipe. Tests both fried-rice (4 steps) and carrot-risotto (7 steps). --- test/Roux/SchemaOrgSpec.hs | 96 ++++++++++++++++++++++++++------------ 1 file changed, 65 insertions(+), 31 deletions(-) diff --git a/test/Roux/SchemaOrgSpec.hs b/test/Roux/SchemaOrgSpec.hs index 78a1b98..9ac91f3 100644 --- a/test/Roux/SchemaOrgSpec.hs +++ b/test/Roux/SchemaOrgSpec.hs @@ -1,7 +1,7 @@ module Roux.SchemaOrgSpec (spec) where import Data.Aeson (Value, decodeStrict) -import Data.CookLang (Duration (..), Metadata (..), Recipe (..)) +import Data.CookLang (Duration (..), Metadata (..), Recipe (..), Section (..), SectionBodyItem (..)) import Data.List.NonEmpty (toList) import Data.Maybe (fromJust, isJust) import Data.Text (Text) @@ -66,34 +66,68 @@ spec = describe "SchemaOrg" $ do it "human-readable -> Nothing" $ chk "20 minutes" Nothing describe "schemaOrgToCooklang" $ do - it "converts fried-rice schema.org recipe to Cooklang" $ do - html <- readFile "test-data/fried-rice.html" - let decoded = tryDecode (T.pack html) - recipe = parseSchemaOrgRecipe (fromJust decoded) - case recipe of - Just r -> case schemaOrgToCooklang r of - Right cook -> do - metaTitle (recipeMetadata cook) `shouldBe` Just "Fried Rice" - metaSource (recipeMetadata cook) `shouldBe` Just "https://cooking.nytimes.com/recipes/12177-fried-rice" - metaCourse (recipeMetadata cook) `shouldBe` Just "one pot, side dish" - metaCuisine (recipeMetadata cook) `shouldBe` Just "asian" - metaTotalTime (recipeMetadata cook) `shouldBe` Just (Duration 20 (Just "minutes")) - metaTags (recipeMetadata cook) `shouldBe` ["egg", "rice", "vegetarian"] - metaDescription (recipeMetadata cook) `shouldSatisfy` isJust - let sections = toList (recipeSections cook) - length sections `shouldBe` 2 - Left err -> fail err - Nothing -> fail "Failed to parse schema.org recipe" + let loadRecipeFile :: FilePath -> IO (Maybe SchemaOrgRecipe) + loadRecipeFile path = do + html <- readFile path + let decoded = tryDecode (T.pack html) + pure (parseSchemaOrgRecipe =<< decoded) - it "converts carrot-risotto schema.org recipe to Cooklang" $ do - html <- readFile "test-data/carrot-risotto.html" - let decoded = tryDecode (T.pack html) - recipe = parseSchemaOrgRecipe (fromJust decoded) - case recipe of - Just r -> case schemaOrgToCooklang r of - Right cook -> do - metaTitle (recipeMetadata cook) `shouldBe` Just "Carrot Risotto With Chile Crisp" - metaAuthor (recipeMetadata cook) `shouldBe` Just "Alexa Weibel" - metaTags (recipeMetadata cook) `shouldSatisfy` (not . null) - Left err -> fail err - Nothing -> fail "Failed to parse schema.org recipe" + let countMethodSteps :: Recipe -> Int + countMethodSteps recipe = + let sections = toList (recipeSections recipe) + in case filter (\s -> sectionName s == Just "Method") sections of + [s] -> + let bodyItems = toList (sectionBody s) + in length [() | SecStep _ <- bodyItems] + _ -> 0 + + let checkStepCount :: FilePath -> String -> Int -> Spec + checkStepCount path label expectedSteps = it ("preserves " <> label <> " step count in Cooklang conversion") $ do + mRecipe <- loadRecipeFile path + case mRecipe of + Nothing -> fail "Failed to parse schema.org recipe" + Just r -> do + let rawInstructions = soRecipeInstructions r + length rawInstructions `shouldBe` expectedSteps + case schemaOrgToCooklang r of + Left err -> fail err + Right cook -> do + let cookSteps = countMethodSteps cook + cookSteps `shouldBe` expectedSteps + + let checkMetadata :: FilePath -> String -> (SchemaOrgRecipe -> IO ()) -> Spec + checkMetadata path label checks = it ("converts " <> label <> " schema.org recipe to Cooklang") $ do + mRecipe <- loadRecipeFile path + case mRecipe of + Nothing -> fail "Failed to parse schema.org recipe" + Just r -> do + checks r + case schemaOrgToCooklang r of + Left err -> fail err + Right _ -> pure () + + checkStepCount "test-data/fried-rice.html" "fried-rice" 4 + + checkStepCount "test-data/carrot-risotto.html" "carrot-risotto" 7 + + checkMetadata "test-data/fried-rice.html" "fried-rice" $ \r -> + case schemaOrgToCooklang r of + Left err -> fail err + Right cook -> do + metaTitle (recipeMetadata cook) `shouldBe` Just "Fried Rice" + metaSource (recipeMetadata cook) `shouldBe` Just "https://cooking.nytimes.com/recipes/12177-fried-rice" + metaCourse (recipeMetadata cook) `shouldBe` Just "one pot, side dish" + metaCuisine (recipeMetadata cook) `shouldBe` Just "asian" + metaTotalTime (recipeMetadata cook) `shouldBe` Just (Duration 20 (Just "minutes")) + metaTags (recipeMetadata cook) `shouldBe` ["egg", "rice", "vegetarian"] + metaDescription (recipeMetadata cook) `shouldSatisfy` isJust + let sections = toList (recipeSections cook) + length sections `shouldBe` 2 + + checkMetadata "test-data/carrot-risotto.html" "carrot-risotto" $ \r -> + case schemaOrgToCooklang r of + Left err -> fail err + Right cook -> do + metaTitle (recipeMetadata cook) `shouldBe` Just "Carrot Risotto With Chile Crisp" + metaAuthor (recipeMetadata cook) `shouldBe` Just "Alexa Weibel" + metaTags (recipeMetadata cook) `shouldSatisfy` (not . null)