{-# LANGUAGE CPP #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE OverloadedStrings #-} module Data.Yaml.IncludeSpec (main, spec) where import Test.Hspec import Data.List (isPrefixOf) import qualified Data.ByteString.Lazy as LB import Data.Aeson #if MIN_VERSION_aeson(2,2,0) import Data.Aeson.Types (JSONPathElement(..)) #else import Data.Aeson.Internal (JSONPathElement(..)) #endif import Data.Yaml (ParseException(InvalidYaml)) import Data.Yaml.Include import Data.Yaml.Internal import Text.Libyaml (YamlException(YamlException)) import Test.Mockery.Directory import Text.RawString.QQ import Data.Yaml.TH (yamlQQ) main :: IO () main = hspec spec asInt :: Int -> Int asInt = id spec :: Spec spec = do describe "decodeFile" $ do it "supports includes" $ do decodeFile "test/resources/foo.yaml" `shouldReturn` Just (object [ "foo" .= asInt 23 , "bar" .= object [ "one" .= asInt 1 , "two" .= asInt 2 ] , "baz" .= asInt 42 ]) it "supports recursive includes" $ do decodeFile "test/resources/baz.yaml" `shouldReturn` Just (object [ "foo" .= object [ "foo" .= asInt 23 , "bar" .= object [ "one" .= asInt 1 , "two" .= asInt 2 ] , "baz" .= asInt 42 ] ]) it "aborts on cyclic includes" $ do (decodeFile "test/resources/loop/foo.yaml" :: IO (Maybe Value)) `shouldThrow` anyException context "when file does not exist" $ do it "throws Left (InvalidYaml (Just (YamlException \"Yaml file not found: ...\")))" $ do (decodeFile "./does_not_exist.yaml" :: IO (Maybe Value)) `shouldThrow` isYamlFileNotFoundException context "with a 1K stack size limit" $ around_ inTempDirectory $ do context "with a large list" $ do it "succeeds" $ do let xs :: [Value] xs = replicate 5000 (Number 23) LB.writeFile "foo.yaml" (encode xs) decodeFile "foo.yaml" `shouldReturn` Just xs describe "decodeFileEither" $ do context "when file does not exist" $ do it "returns Left (InvalidYaml (Just (YamlException \"Yaml file not found: ...\")))" $ do (decodeFileEither "./does_not_exist.yaml" :: IO (Either ParseException Value)) >>= (`shouldSatisfy` either isYamlFileNotFoundException (const False)) describe "decodeFileWithWarnings" $ around_ inTempDirectory $ do it "warns on duplicate keys" $ do writeFile "foo.yaml" [r| foo: 23 foo: bar |] Right result <- decodeFileWithWarnings "foo.yaml" result `shouldBe` ([DuplicateKey [Key "foo"]], [yamlQQ| foo: bar |]) it "warns on nested duplicate keys" $ do writeFile "foo.yaml" [r| foo: - 42 - bar: 23 bar: baz |] Right result <- decodeFileWithWarnings "foo.yaml" result `shouldBe` ([DuplicateKey [Key "foo", Index 1, Key "bar"]], [yamlQQ| foo: - 42 - bar: baz |]) context "when overriding a merged key" $ do it "does not warn" $ do writeFile "foo.yaml" [r| foo-1: &my-ref bar: 23 foo-2: <<: *my-ref bar: 42 |] Right result <- decodeFileWithWarnings "foo.yaml" result `shouldBe` ([], [yamlQQ| foo-1: bar: 23 foo-2: bar: 42 |]) context "when overriding twice" $ do it "warns" $ do writeFile "foo.yaml" [r| foo-1: &my-ref bar: 23 foo-2: <<: *my-ref bar: 42 bar: 65 |] Right result <- decodeFileWithWarnings "foo.yaml" result `shouldBe` ([DuplicateKey [Key "foo-2", Key "bar"]], [yamlQQ| foo-1: bar: 23 foo-2: bar: 65 |]) isYamlFileNotFoundException :: ParseException -> Bool isYamlFileNotFoundException (InvalidYaml (Just (YamlException msg))) | "Yaml file not found: " `isPrefixOf` msg = True isYamlFileNotFoundException _ = False