graphql-api-0.1.2: tests/ASTTests.hs
{-# 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