graphql-api-0.4.0: tests/ResolverSpec.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
module ResolverSpec (spec) where
import Protolude hiding (Enum)
import Test.Hspec
import Data.Aeson (encode, toJSON, object, (.=), Value(Null))
import GraphQL
( Response(..)
, interpretAnonymousQuery
)
import GraphQL.API
( Object
, Field
, Argument
, Enum
, List
, (:>)
)
import GraphQL.Resolver
( Handler
, ResolverError(..)
, (:<>)(..)
, returns
, handlerError
)
import GraphQL.Internal.Output (singleError)
import qualified GraphQL.Value as GValue
import EnumTests ( Mode(NormalFile) )
-- Test a custom error monad
type TMonad = ExceptT Text IO
type T = Object "T" '[] '[ Field "z" Int32
, Argument "x" Int32 :> Field "t" Int32
, Argument "y" Int32 :> Field "q" (Maybe Int32)
, Argument "d" Double :> Field "r" Double
, Field "l" (List Int32)
, Argument "n" Text :> Field "foo" (Maybe Foo)
, Field "bar" (Maybe Foo)
]
tHandler :: Handler TMonad T
tHandler = pure $
returns 10
:<> (\x -> if x == 99 then handlerError "missed 99th value" else returns x)
:<> returns . Just . (returns . (*2))
:<> (\dArg -> if dArg == 9.9 then handlerError "bad 9.9 value" else returns dArg)
:<> returns ([ returns 0, returns 7, handlerError "no number 9" ])
:<> (\_nArg -> returns $ Just $ return $ returns "fred")
:<> returns Nothing
-- https://github.com/jml/graphql-api/issues/119
-- Maybe X didn't descend into its argument. Now it does.
type Query = Object "Query" '[]
'[ Argument "id" Text :> Field "test" (Maybe Foo) ]
type Foo = Object "Foo" '[]
'[ Field "name" Text ]
data ServerFoo = ServerFoo
{ name :: Text
} deriving (Eq, Show)
lookupFoo :: Text -> IO (Maybe ServerFoo)
lookupFoo _ = pure $ Just (ServerFoo "Mort")
viewFoo :: ServerFoo -> Handler IO Foo
viewFoo ServerFoo { name=name } = pure $ returns $ name
handler :: Handler IO Query
handler = pure $ \fooId -> do
foo <- lookupFoo fooId
returns $ viewFoo <$> foo
-- Enum test
type EnumQuery = Object "File" '[]
'[ Field "mode" (Enum "modeEnumName" Mode) ]
enumHandler :: Handler IO EnumQuery
enumHandler = pure $ returns NormalFile
enumHandler2 :: Handler IO EnumQuery
enumHandler2 = pure $ handlerError "I forgot!"
-- /Enum test
spec :: Spec
spec = describe "TypeAPI" $ do
describe "tTest" $ do
it "works in a simple Int32 case" $ do
Right (Success obj) <- runExceptT (interpretAnonymousQuery @T tHandler "{ t(x: 12) }")
encode obj `shouldBe` "{\"t\":12}"
it "works in a simple Double case" $ do
r <- runExceptT (interpretAnonymousQuery @T tHandler "{ r(d: 1.2) }")
case r of
Right (Success obj) -> encode obj `shouldBe` "{\"r\":1.2}"
_ -> r `shouldNotBe` r
it "works for value and error list elements" $ do
r <- runExceptT (interpretAnonymousQuery @T tHandler "{ l }")
case r of
Right (PartialSuccess obj err) -> do
encode obj `shouldBe` "{\"l\":[0,7,null]}"
err `shouldBe` (singleError (HandlerError "no number 9"))
_ -> r `shouldNotBe` r
it "works for Nullable present elements" $ do
r <- runExceptT (interpretAnonymousQuery @T tHandler "{ foo(n: \"flintstone\") { name } }")
case r of
Right (Success obj) -> do
encode obj `shouldBe` "{\"foo\":{\"name\":\"fred\"}}"
_ -> r `shouldNotBe` r
it "works for Nullable null elements" $ do
r <- runExceptT (interpretAnonymousQuery @T tHandler "{ bar { name } }")
case r of
Right (Success obj) -> do
encode obj `shouldBe` "{\"bar\":null}"
_ -> r `shouldNotBe` r
it "complains about a missing field" $ do
Right (PartialSuccess _ errs) <- runExceptT (interpretAnonymousQuery @T tHandler "{ not_a_field }")
errs `shouldBe` singleError (FieldNotFoundError "not_a_field")
it "complains about a handler throwing an exception" $ do
r <- runExceptT (interpretAnonymousQuery @T tHandler "{ t(x: 99) }")
case r of
Right (PartialSuccess v errs) -> do
-- n.b. this hasn't gone through the final JSON embedding,
-- so it's the individual components instead of the final
-- response of '{ "data": ..., "errors": ... }'
errs `shouldBe` (singleError (HandlerError "missed 99th value"))
toJSON (GValue.toValue v) `shouldBe` object [ "t" .= Null ]
_ -> r `shouldNotBe` r
it "complains about missing argument" $ do
Right (PartialSuccess _ errs) <- runExceptT (interpretAnonymousQuery @T tHandler "{ t }")
errs `shouldBe` singleError (ValueMissing "x")
describe "issue 119" $ do
it "Just works" $ do
Success obj <- interpretAnonymousQuery @Query handler "{ test(id: \"10\") { name } }"
encode obj `shouldBe` "{\"test\":{\"name\":\"Mort\"}}"
describe "Parse, validate and execute queries against API" $ do
it "API.Enum works" $ do
Success obj <- interpretAnonymousQuery @EnumQuery enumHandler "{ mode }"
encode obj `shouldBe` "{\"mode\":\"NormalFile\"}"
it "API.Enum handles errors" $ do
r <- interpretAnonymousQuery @EnumQuery enumHandler2 "{ mode }"
case r of
(PartialSuccess obj errs) -> do
encode obj `shouldBe` "{\"mode\":null}"
errs `shouldBe` (singleError $ HandlerError "I forgot!")
_ -> r `shouldNotBe` r