{-# LANGUAGE QuasiQuotes #-} -- | Tests for AST, including parser and encoder. module ASTTests (tests) where import Protolude import Data.Attoparsec.Text (parseOnly) import Text.RawString.QQ (r) import Test.Hspec.QuickCheck (prop) import Test.QuickCheck (arbitrary, forAll, resize) import Test.Tasty (TestTree) import Test.Tasty.Hspec (testSpec, describe, it, shouldBe) import GraphQL.Value (String(..)) import GraphQL.Internal.Name (Name) import qualified GraphQL.Internal.Syntax.AST as AST import qualified GraphQL.Internal.Syntax.Parser as Parser import qualified GraphQL.Internal.Syntax.Encoder as Encoder kitchenSink :: Text kitchenSink = "query queryName($foo:ComplexType,$site:Site=MOBILE){whoever123is:node(id:[123,456]){id,... on User@defer{field2{id,alias:field1(first:10,after:$foo)@include(if:$foo){id,...frag}}}}}mutation likeStory{like(story:123)@defer{story{id}}}fragment frag on Friend{foo(size:$size,bar:$b,obj:{key:\"value\"})}\n" dog :: Name dog = "dog" someName :: Name someName = "name" tests :: IO TestTree tests = testSpec "AST" $ do describe "Parser and encoder" $ do it "roundtrips on minified documents" $ do let actual = Encoder.queryDocument <$> parseOnly Parser.queryDocument kitchenSink actual `shouldBe` Right kitchenSink describe "parsing numbers" $ do it "works for some integers" $ do parseOnly Parser.value "1" `shouldBe` Right (AST.ValueInt 1) prop "works for all integers" $ do \x -> parseOnly Parser.value (show x) == Right (AST.ValueInt x) it "works for some floats" $ do parseOnly Parser.value "1.5" `shouldBe` Right (AST.ValueFloat 1.5) it "treats floats as floats even if they end with .0" $ do parseOnly Parser.value "0.0" `shouldBe` Right (AST.ValueFloat 0.0) prop "works for floats" $ do \x -> parseOnly Parser.value (show x) == Right (AST.ValueFloat x) describe "strings" $ do prop "works for all strings" $ do \(String x) -> let input = AST.ValueString (AST.StringValue x) output = Encoder.value input in parseOnly Parser.value output == Right input it "handles unusual strings" $ do let input = AST.ValueString (AST.StringValue "\fh\244") let output = Encoder.value input -- \f is \u000c output `shouldBe` "\"\\u000ch\244\"" parseOnly Parser.value output `shouldBe` Right input describe "parsing values" $ do prop "works for all literal values" $ do forAll (resize 3 arbitrary) $ \x -> parseOnly Parser.value (Encoder.value x) `shouldBe` Right x it "parses ununusual objects" $ do let input = AST.ValueObject (AST.ObjectValue [ AST.ObjectField "s" (AST.ValueString (AST.StringValue "\224\225v^6{FPDk\DC3\a")), AST.ObjectField "Hsr" (AST.ValueInt 0) ]) let output = Encoder.value input parseOnly Parser.value output `shouldBe` Right input it "parses lists of floats" $ do let input = AST.ValueList (AST.ListValue [ AST.ValueFloat 1.5 , AST.ValueFloat 1.5 ]) let output = Encoder.value input output `shouldBe` "[1.5,1.5]" parseOnly Parser.value output `shouldBe` Right input describe "Parser" $ do it "parses anonymous query documents" $ do let query = [r|{ dog { name } }|] let Right parsed = parseOnly Parser.queryDocument query let expected = AST.QueryDocument [ AST.DefinitionOperation (AST.AnonymousQuery [ AST.SelectionField (AST.Field Nothing dog [] [] [ AST.SelectionField (AST.Field Nothing someName [] [] []) ]) ]) ] parsed `shouldBe` expected it "parses invalid documents" $ do let query = [r|{ dog { name } } query getName { dog { owner { name } } }|] let Right parsed = parseOnly Parser.queryDocument query let expected = AST.QueryDocument [ AST.DefinitionOperation (AST.AnonymousQuery [ AST.SelectionField (AST.Field Nothing dog [] [] [ AST.SelectionField (AST.Field Nothing someName [] [] []) ]) ]) , AST.DefinitionOperation (AST.Query (AST.Node "getName" [] [] [ AST.SelectionField (AST.Field Nothing dog [] [] [ AST.SelectionField (AST.Field Nothing "owner" [] [] [ AST.SelectionField (AST.Field Nothing someName [] [] []) ]) ]) ])) ] parsed `shouldBe` expected it "includes variable definitions" $ do let query = [r| query houseTrainedQuery($atOtherHomes: Boolean = true) { dog { isHousetrained(atOtherHomes: $atOtherHomes) } } |] let Right parsed = parseOnly Parser.queryDocument query let expected = AST.QueryDocument [ AST.DefinitionOperation (AST.Query (AST.Node "houseTrainedQuery" [ AST.VariableDefinition (AST.Variable "atOtherHomes") (AST.TypeNamed (AST.NamedType "Boolean")) (Just (AST.ValueBoolean True)) ] [] [ AST.SelectionField (AST.Field Nothing dog [] [] [ AST.SelectionField (AST.Field Nothing "isHousetrained" [ AST.Argument "atOtherHomes" (AST.ValueVariable (AST.Variable "atOtherHomes")) ] [] []) ]) ])) ] parsed `shouldBe` expected