packages feed

language-hcl-0.1.1.0: test/Data/HCLSpec.hs

{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE OverloadedLists   #-}
{-# LANGUAGE OverloadedStrings #-}
module Data.HCLSpec where

import           Control.Monad
import           Control.Monad.IO.Class
import           Data.HCL
import           Data.HCL.TestHelper
import           Data.Text              (Text)
import qualified Data.Text              as Text
import qualified Data.Text.IO           as Text
import           System.Directory
import           System.FilePath
import           System.IO.Unsafe
import           Test.Hspec
import           Text.Megaparsec        (Parsec, Token (..), runParser)

{-# NOINLINE fs' #-}
fs' :: [FilePath]
fs' = unsafePerformIO $ do
    fs <- liftIO (getDirectoryContents "./test-fixtures")
    return $ filter ((== ".hcl") . takeExtension) fs

spec :: Spec
spec = do
    describe "stringParts" $ do
        it "parses normal strings" $ do
            let input = "\"something\""
            testParser stringParts input [HCLStringPlain "something"]

        it "parses interpolated strings" $ do
            let input = "\"${asdf} Hello World asdfasdf ${hey}\""
            testParser stringParts input [ HCLStringInterp "asdf"
                                         , HCLStringPlain " Hello World asdfasdf "
                                         , HCLStringInterp "hey"
                                         ]

    describe "ident" $ do
        it "parses alphanum" $ do
            let input = "asdf"
            testParser ident input "asdf"

        it "parses dashes" $ do
            let input = "asdf-asdf"
            testParser ident input "asdf-asdf"

        it "parses underscores" $ do
            let input = "asdf_asdf"
            testParser ident input "asdf_asdf"

        it "stops at whitespace" $ do
            let input = "asdf asdf"
            testParser ident input "asdf"

    describe "stringPlain" $ do
        it "parses the empty string" $ do
            let input = ""
            testParser stringPlain input ""

        it "parses charaters" $ do
            let input = "something"
            testParser stringPlain input "something"

        it "parses escape sequences" $ do
            let input = "bar\\\"baz\\n"
            testParser stringPlain input "bar\"baz\n"

    describe "stringPlainMultiline" $
        it "parses multiline strings" $ do
            let input = Text.unlines [ "<<EOF"
                                     , "stuff"
                                     , "here"
                                     , "EOF"
                                     ]
            testParser stringPlainMultiline input "stuff\nhere"

    describe "assignment" $ do
        it "parses weird string assignments (escaping)" $ do
            let input = "foo = \"bar\\\"\\n\""
            testParser assignment input (["foo"], bplain "bar\"\n")

        it "parses HashiCorp's escape.hcl" $ do
            input <- Text.readFile "./test-fixtures/escape.hcl"
            testParser assignment input (["foo"], bplain "bar\"baz\\n")

        it "parses string assignments" $ do
            let input = "foo = \"bar\""
            testParser assignment input (["foo"], bplain "bar")

        it "parses assignments to numbers" $ do
            let input = "foo = 10"
            testParser assignment input (["foo"], HCLNumber 10)

        it "parses assignments to lists" $ do
            let input = "foo = [1, 2, 3]"
            testParser assignment input
                (["foo"], HCLList [HCLNumber 1, HCLNumber 2, HCLNumber 3])

        it "parses multiline string assignments" $ do
            let input = Text.unlines [ "bar = <<EOF"
                                     , "hello there"
                                     , "here"
                                     , "EOF"
                                     ]
            testParser assignment input
                (["bar"], bplain "hello there\nhere")

        it "parses nested assignments" $ do
            let input = "foo.bar = 10"
            testParser assignment input
                (["foo", "bar"], HCLNumber 10)

        it "fails to parse empty identifiers" $ do
            let input = " = 10"
            testFailure "" input

        it "parses assignments to objects" $ do
            let input = "foo = { name = \"john\" }"
            testParser assignment input
                (["foo"], HCLObject [] [(["name"], bplain "john")])

    describe "parseHCL" $ do
        it "parses basic assignments" $ do
            let input = Text.unlines [ "foo = \"bar\""
                                     , "bar = \"foo\""
                                     ]
            testParser hcl input
                [ HCLStatementAssignment (["foo"], bplain "bar")
                , HCLStatementAssignment (["bar"], bplain "foo")
                ]

        it "parses multiline string assignments" $ do
            let input = Text.unlines [ "bar = <<EOF"
                                     , "hello there"
                                     , "here"
                                     , "EOF"
                                     ]
            testParser hcl input
                [ HCLStatementAssignment (["bar"], bplain "hello there\nhere")
                ]

        it "parses interpolated assignments" $ do
            let input = Text.unlines [ "foo = \"bar\""
                                     , "bar = \"${file(\"bing/bong.txt\")}\""
                                     ]
            testParser hcl input
                [ HCLStatementAssignment (["foo"], bplain "bar")
                , HCLStatementAssignment (["bar"], binterp "file(\"bing/bong.txt\")")
                ]

        it "parses complex interpolated assignments" $ do
            let input = Text.unlines [ "foo = \"bar\""
                                     , "bar = \"stuff/${file(\"bing/bong.txt\")}\""
                                     ]
            testParser hcl input
                [ HCLStatementAssignment (["foo"], bplain "bar")
                , HCLStatementAssignment ( ["bar"]
                                         , HCLString [ HCLStringPlain "stuff/"
                                                     , HCLStringInterp "file(\"bing/bong.txt\")"
                                                     ]
                                         )
                ]

    describe "Hashicorp Test Suite" $ forM_ fs' $ \fp -> it fp $ do
        inp <- liftIO $ Text.readFile ("test-fixtures" </> fp)
        case fp of
            "unterminated_block_comment.hcl" -> testFailure fp inp
            "multiline_no_marker.hcl" -> testFailure fp inp
            "multiline_bad.hcl" -> testFailure fp inp
            "unterminated_brace.hcl" -> testFailure fp inp
            _ -> case parseHCL fp inp of
                    Left e -> error (show e)
                    Right _ -> True `shouldBe` True