module LoadEnv.ParseSpec
    ( spec
    ) where

import LoadEnv.Parse
import Test.Hspec
import Text.Parsec (parse)

spec :: Spec
spec = do
    describe "parseEnvironment" $ do
        it "parses variable declarations among comments and blank lines" $ do
            let env = unlines
                    [ "# An environment file"
                    , "FOO=bar"
                    , "BAZ=\"bat\""
                    , "BAT=\"multi-"
                    , "pass"
                    , "\""
                    , ""
                    , "# vim ft:sh:"
                    ]

            parse parseEnvironment "" env `shouldBe` Right
                [ ("FOO", "bar")
                , ("BAZ", "bat")
                , ("BAT", "multi-\npass\n")
                ]

        it "parses an empty file into an empty list of variables" $ do
            parse parseEnvironment "" "" `shouldBe` Right []


    describe "parseVariable" $ do
        it "reads unquoted variables" $
            parse parseVariable "" "FOO=bar\n" `shouldBe` Right ("FOO", "bar")

        it "reads quoted variables" $ do
            parse parseVariable "" "FOO=\"bar\"\n"
                `shouldBe` Right ("FOO", "bar")
            parse parseVariable "" "FOO='bar'\n"
                `shouldBe` Right ("FOO", "bar")

        it "allows newlines in quoted variables" $ do
            parse parseVariable "" "FOO=\"foo\nbar\"\n"
                `shouldBe` Right ("FOO", "foo\nbar")

        it "handles empty values" $
            parse parseVariable "" "FOO=\n" `shouldBe` Right ("FOO", "")

        it "handles empty quoted values" $ do
            parse parseVariable "" "FOO=\"\"\n" `shouldBe` Right ("FOO", "")
            parse parseVariable "" "FOO=''\n" `shouldBe` Right ("FOO", "")

        it "handles underscored variables" $
            parse parseVariable "" "FOO_BAR=baz\n"
                `shouldBe` Right ("FOO_BAR", "baz")

        it "treats leading spaces as invalid" $
            parse parseVariable "" "  FOO=bar\n"
                `shouldContainError` "unexpected \"F\""

        it "treats spaces around equals as invalid" $
            parse parseVariable "" "FOO = bar\n"
                `shouldContainError` "unexpected \" \""

        it "treats unquoted spaces as invalid" $
            parse parseVariable "" "FOO=bar baz\n"
                `shouldContainError` "unexpected \"b\""

        it "treats unbalanced quotes as invalid" $ do
            parse parseVariable "" "FOO=\"bar\n"
                `shouldContainError` "unexpected end of input"
            parse parseVariable "" "FOO='bar\n"
                `shouldContainError` "unexpected end of input"
            parse parseVariable "" "FOO=bar\"\n"
                `shouldContainError` "unexpected \"\\\"\""
            parse parseVariable "" "FOO=bar'\n"
                `shouldContainError` "unexpected \"\'\""

        it "handles escaped quotes" $ do
            parse parseVariable "" "FOO=\"bar\\\"baz\"\n"
                `shouldBe` Right ("FOO", "bar\"baz")
            parse parseVariable "" "FOO='bar\\'baz'\n"
                `shouldBe` Right ("FOO", "bar'baz")

        it "handles escaped spaces" $
            parse parseVariable "" "FOO=bar\\ baz\n"
                `shouldBe` Right ("FOO", "bar baz")

        it "discards any lines using `export'" $
            parse parseVariable "" "export FOO=bar\n"
                `shouldBe` Right ("FOO", "bar")

        it "requires valid environment variable identifies" $ do
            parse parseVariable "" "S3_KEY=abc123\n"
                `shouldBe` Right ("S3_KEY", "abc123")
            parse parseVariable "" "_S3_KEY=abc123\n"
                `shouldBe` Right ("_S3_KEY", "abc123")
            parse parseVariable "" "S3_key=abc123\n"
                `shouldBe` Right ("S3_key", "abc123")
            parse parseVariable "" "s3_key=abc123\n"
                `shouldBe` Right ("s3_key", "abc123")

            parse parseVariable "" "S3~KEY=abc123\n"
                `shouldContainError` "unexpected \"~\""
            parse parseVariable "" "S3-KEY=abc123\n"
                `shouldContainError` "unexpected \"-\""
            parse parseVariable "" "3_KEY=abc123\n"
                `shouldContainError` "unexpected \"3\""

shouldContainError :: Show a => Either a b -> String -> Expectation
v `shouldContainError` msg = either
    (\e -> show e `shouldContain` msg)
    (\_ -> expectationFailure "Expected no parse") v
