test: add step count preservation tests for SchemaOrg → Cooklang conversion
Build and Deploy / build-and-deploy (push) Successful in 1m14s

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).
This commit is contained in:
2026-05-20 15:57:14 -04:00
parent 6081534c10
commit c33a51d80b
+65 -31
View File
@@ -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)