forked from OSS/graphql
		
	add Arbitrary instances for AST.Document, add random arguments Parser test
This commit is contained in:
		@@ -12,6 +12,9 @@ and this project adheres to
 | 
			
		||||
  swapped).
 | 
			
		||||
- Parsing empty list as an argument.
 | 
			
		||||
 | 
			
		||||
### Added
 | 
			
		||||
- quickCheck Parser test for arguments. Arbitrary instances for Language.GraphQL.AST.Document.
 | 
			
		||||
 | 
			
		||||
## [1.0.2.0] - 2021-12-26
 | 
			
		||||
### Added
 | 
			
		||||
- `Serialize` instance for `Type.Definition.Value`.
 | 
			
		||||
 
 | 
			
		||||
@@ -93,6 +93,7 @@ test-suite graphql-test
 | 
			
		||||
    Language.GraphQL.AST.EncoderSpec
 | 
			
		||||
    Language.GraphQL.AST.LexerSpec
 | 
			
		||||
    Language.GraphQL.AST.ParserSpec
 | 
			
		||||
    Language.GraphQL.AST.Arbitrary
 | 
			
		||||
    Language.GraphQL.ErrorSpec
 | 
			
		||||
    Language.GraphQL.Execute.CoerceSpec
 | 
			
		||||
    Language.GraphQL.Execute.OrderedMapSpec
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										99
									
								
								tests/Language/GraphQL/AST/Arbitrary.hs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										99
									
								
								tests/Language/GraphQL/AST/Arbitrary.hs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,99 @@
 | 
			
		||||
{-# LANGUAGE OverloadedStrings #-}
 | 
			
		||||
 | 
			
		||||
module Language.GraphQL.AST.Arbitrary where
 | 
			
		||||
 | 
			
		||||
import qualified Language.GraphQL.AST.Document as Doc
 | 
			
		||||
import Test.QuickCheck.Arbitrary (Arbitrary (arbitrary))
 | 
			
		||||
import Test.QuickCheck (oneof, elements, listOf, resize, NonEmptyList (..))
 | 
			
		||||
import Test.QuickCheck.Gen (Gen (..))
 | 
			
		||||
import Data.Text (Text, pack)
 | 
			
		||||
 | 
			
		||||
newtype AnyPrintableChar = AnyPrintableChar { getAnyPrintableChar :: Char } deriving (Eq, Show)
 | 
			
		||||
 | 
			
		||||
alpha :: String
 | 
			
		||||
alpha = ['a'..'z'] <> ['A'..'Z']
 | 
			
		||||
 | 
			
		||||
num :: String
 | 
			
		||||
num = ['0'..'9']
 | 
			
		||||
 | 
			
		||||
instance Arbitrary AnyPrintableChar where
 | 
			
		||||
    arbitrary = AnyPrintableChar <$> elements chars
 | 
			
		||||
        where
 | 
			
		||||
           chars = alpha <> num <> ['_'] 
 | 
			
		||||
 | 
			
		||||
newtype AnyPrintableText = AnyPrintableText { getAnyPrintableText :: Text } deriving (Eq, Show)
 | 
			
		||||
 | 
			
		||||
instance Arbitrary AnyPrintableText where
 | 
			
		||||
    arbitrary = do
 | 
			
		||||
        nonEmptyStr <- getNonEmpty <$> (arbitrary :: Gen (NonEmptyList AnyPrintableChar))
 | 
			
		||||
        pure $ AnyPrintableText (pack $ map getAnyPrintableChar nonEmptyStr)
 | 
			
		||||
 | 
			
		||||
-- https://spec.graphql.org/June2018/#Name
 | 
			
		||||
newtype AnyName = AnyName { getAnyName :: Text } deriving (Eq, Show)
 | 
			
		||||
 | 
			
		||||
instance Arbitrary AnyName where
 | 
			
		||||
    arbitrary = do
 | 
			
		||||
        firstChar <- elements $ alpha <> ['_']
 | 
			
		||||
        rest <- (arbitrary :: Gen [AnyPrintableChar])
 | 
			
		||||
        pure $ AnyName (pack $ firstChar : map getAnyPrintableChar rest)
 | 
			
		||||
 | 
			
		||||
newtype AnyLocation = AnyLocation { getAnyLocation :: Doc.Location } deriving (Eq, Show)
 | 
			
		||||
 | 
			
		||||
instance Arbitrary AnyLocation where
 | 
			
		||||
    arbitrary = AnyLocation <$> (Doc.Location <$> arbitrary <*> arbitrary)
 | 
			
		||||
 | 
			
		||||
newtype AnyNode a = AnyNode { getAnyNode :: Doc.Node a } deriving (Eq, Show)
 | 
			
		||||
 | 
			
		||||
instance Arbitrary a => Arbitrary (AnyNode a) where
 | 
			
		||||
    arbitrary = do
 | 
			
		||||
        (AnyLocation location') <- arbitrary
 | 
			
		||||
        node' <- flip Doc.Node location' <$> arbitrary
 | 
			
		||||
        pure $ AnyNode node'
 | 
			
		||||
 | 
			
		||||
newtype AnyObjectField a = AnyObjectField { getAnyObjectField :: Doc.ObjectField a } deriving (Eq, Show)
 | 
			
		||||
 | 
			
		||||
instance Arbitrary a => Arbitrary (AnyObjectField a) where
 | 
			
		||||
    arbitrary = do
 | 
			
		||||
        name' <- getAnyName <$> arbitrary
 | 
			
		||||
        value' <- getAnyNode <$> arbitrary
 | 
			
		||||
        location' <- getAnyLocation <$> arbitrary
 | 
			
		||||
        pure $ AnyObjectField $ Doc.ObjectField name' value' location'
 | 
			
		||||
 | 
			
		||||
newtype AnyValue = AnyValue { getAnyValue :: Doc.Value } deriving (Eq, Show)
 | 
			
		||||
 | 
			
		||||
instance Arbitrary AnyValue where
 | 
			
		||||
    arbitrary = AnyValue <$> oneof
 | 
			
		||||
        [ variableGen
 | 
			
		||||
        , Doc.Int <$> arbitrary
 | 
			
		||||
        , Doc.Float <$> arbitrary
 | 
			
		||||
        , Doc.String <$> (getAnyPrintableText <$> arbitrary)
 | 
			
		||||
        , Doc.Boolean <$> arbitrary
 | 
			
		||||
        , MkGen $ \_ _ -> Doc.Null
 | 
			
		||||
        , Doc.Enum <$> (getAnyName <$> arbitrary)
 | 
			
		||||
        , Doc.List <$> listGen
 | 
			
		||||
        , Doc.Object <$> objectGen
 | 
			
		||||
        ]
 | 
			
		||||
            where
 | 
			
		||||
                variableGen :: Gen Doc.Value
 | 
			
		||||
                variableGen = Doc.Variable <$> (getAnyName <$> arbitrary)
 | 
			
		||||
                listGen :: Gen [Doc.Node Doc.Value]
 | 
			
		||||
                listGen = (resize 5 . listOf) nodeGen
 | 
			
		||||
                nodeGen = do
 | 
			
		||||
                    node' <- getAnyNode <$> (arbitrary :: Gen (AnyNode AnyValue))
 | 
			
		||||
                    pure (getAnyValue <$> node')
 | 
			
		||||
                objectGen :: Gen [Doc.ObjectField Doc.Value]
 | 
			
		||||
                objectGen = resize 1 $ do
 | 
			
		||||
                    list <- getNonEmpty <$> (arbitrary :: Gen (NonEmptyList (AnyObjectField AnyValue)))
 | 
			
		||||
                    pure $ map (fmap getAnyValue . getAnyObjectField) list
 | 
			
		||||
 | 
			
		||||
newtype AnyArgument a = AnyArgument { getAnyArgument :: Doc.Argument } deriving (Eq, Show)
 | 
			
		||||
 | 
			
		||||
instance Arbitrary a => Arbitrary (AnyArgument a) where
 | 
			
		||||
    arbitrary = do
 | 
			
		||||
        name' <- getAnyName <$> arbitrary
 | 
			
		||||
        (AnyValue value') <- arbitrary
 | 
			
		||||
        (AnyLocation location') <- arbitrary
 | 
			
		||||
        pure $ AnyArgument $ Doc.Argument name' (Doc.Node value' location') location'
 | 
			
		||||
 | 
			
		||||
printArgument :: AnyArgument AnyValue -> Text
 | 
			
		||||
printArgument (AnyArgument (Doc.Argument name' (Doc.Node value' _) _)) = name' <> ": " <> (pack . show) value'
 | 
			
		||||
@@ -5,6 +5,8 @@ module Language.GraphQL.AST.ParserSpec
 | 
			
		||||
    ) where
 | 
			
		||||
 | 
			
		||||
import Data.List.NonEmpty (NonEmpty(..))
 | 
			
		||||
import Data.Text (Text)
 | 
			
		||||
import qualified Data.Text as Text
 | 
			
		||||
import Language.GraphQL.AST.Document
 | 
			
		||||
import qualified Language.GraphQL.AST.DirectiveLocation as DirLoc
 | 
			
		||||
import Language.GraphQL.AST.Parser
 | 
			
		||||
@@ -12,6 +14,8 @@ import Language.GraphQL.TH
 | 
			
		||||
import Test.Hspec (Spec, describe, it, context)
 | 
			
		||||
import Test.Hspec.Megaparsec (shouldParse, shouldFailOn, shouldSucceedOn)
 | 
			
		||||
import Text.Megaparsec (parse)
 | 
			
		||||
import Test.QuickCheck (property, NonEmptyList (..), mapSize)
 | 
			
		||||
import Language.GraphQL.AST.Arbitrary
 | 
			
		||||
 | 
			
		||||
spec :: Spec
 | 
			
		||||
spec = describe "Parser" $ do
 | 
			
		||||
@@ -29,7 +33,7 @@ spec = describe "Parser" $ do
 | 
			
		||||
                 hello(text: "Argument")
 | 
			
		||||
               }|]
 | 
			
		||||
 | 
			
		||||
       it "accepts int as argument" $
 | 
			
		||||
       it "accepts int as argument1" $
 | 
			
		||||
           parse document "" `shouldSucceedOn` [gql|{
 | 
			
		||||
                 user(id: 4)
 | 
			
		||||
               }|]
 | 
			
		||||
@@ -67,7 +71,12 @@ spec = describe "Parser" $ do
 | 
			
		||||
                 test(username: """username""", password: """password""")
 | 
			
		||||
               }|]
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
       it "accepts any arguments" $ mapSize (const 10) $ property $ \xs ->
 | 
			
		||||
            let
 | 
			
		||||
                query' :: Text
 | 
			
		||||
                arguments = map printArgument $ getNonEmpty (xs :: NonEmptyList (AnyArgument AnyValue))
 | 
			
		||||
                query' = "query(" <> Text.intercalate ", " arguments <> ")" in
 | 
			
		||||
            parse document "" `shouldSucceedOn` ("{ " <> query' <> " }")
 | 
			
		||||
 | 
			
		||||
    it "parses minimal schema definition" $
 | 
			
		||||
        parse document "" `shouldSucceedOn` [gql|schema { query: Query }|]
 | 
			
		||||
@@ -118,16 +127,6 @@ spec = describe "Parser" $ do
 | 
			
		||||
            }
 | 
			
		||||
        |]
 | 
			
		||||
 | 
			
		||||
    it "parses minimal enum type definition" $
 | 
			
		||||
        parse document "" `shouldSucceedOn` [gql|
 | 
			
		||||
            enum Direction {
 | 
			
		||||
              NORTH
 | 
			
		||||
              EAST
 | 
			
		||||
              SOUTH
 | 
			
		||||
              WEST
 | 
			
		||||
            }
 | 
			
		||||
        |]
 | 
			
		||||
 | 
			
		||||
    it "parses minimal input object type definition" $
 | 
			
		||||
        parse document "" `shouldSucceedOn` [gql|
 | 
			
		||||
            input Point2D {
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user