From eb40810f25a2ab29e24c64f1e3c82b3b590460c8 Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Wed, 10 Jul 2019 05:57:35 +0200 Subject: Replace tasty and HUnit with Hspec --- tests/Test/StarWars/QuerySpec.hs | 351 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 351 insertions(+) create mode 100644 tests/Test/StarWars/QuerySpec.hs (limited to 'tests/Test/StarWars/QuerySpec.hs') diff --git a/tests/Test/StarWars/QuerySpec.hs b/tests/Test/StarWars/QuerySpec.hs new file mode 100644 index 0000000..0f6a2ef --- /dev/null +++ b/tests/Test/StarWars/QuerySpec.hs @@ -0,0 +1,351 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE QuasiQuotes #-} +module Test.StarWars.QuerySpec + ( spec + ) where + +import qualified Data.Aeson as Aeson +import Data.Aeson ( object + , (.=) + ) +import Data.Text (Text) +import Language.GraphQL +import Language.GraphQL.Schema (Subs) +import Text.RawString.QQ (r) +import Test.Hspec.Expectations ( Expectation + , shouldBe + ) +import Test.Hspec ( Spec + , describe + , it + ) +import Test.StarWars.Schema + +-- * Test +-- See https://github.com/graphql/graphql-js/blob/master/src/__tests__/starWarsQueryTests.js + +spec :: Spec +spec = describe "Star Wars Query Tests" $ do + describe "Basic Queries" $ do + it "R2-D2 hero" $ testQuery + [r| query HeroNameQuery { + hero { + id + } + } + |] + $ object [ "data" .= object ["hero" .= object ["id" .= ("2001" :: Text)]]] + it "R2-D2 ID and friends" $ testQuery + [r| query HeroNameAndFriendsQuery { + hero { + id + name + friends { + name + } + } + } + |] + $ object [ "data" .= object [ + "hero" .= object + [ "id" .= ("2001" :: Text) + , r2d2Name + , "friends" .= + [ object [lukeName] + , object [hanName] + , object [leiaName] + ] + ] + ]] + + describe "Nested Queries" $ do + it "R2-D2 friends" $ testQuery + [r| query NestedQuery { + hero { + name + friends { + name + appearsIn + friends { + name + } + } + } + } + |] + $ object [ "data" .= object [ + "hero" .= object [ + "name" .= ("R2-D2" :: Text) + , "friends" .= [ + object [ + "name" .= ("Luke Skywalker" :: Text) + , "appearsIn" .= ["NEWHOPE","EMPIRE","JEDI" :: Text] + , "friends" .= [ + object [hanName] + , object [leiaName] + , object [c3poName] + , object [r2d2Name] + ] + ] + , object [ + hanName + , "appearsIn" .= [ "NEWHOPE","EMPIRE","JEDI" :: Text] + , "friends" .= [ + object [lukeName] + , object [leiaName] + , object [r2d2Name] + ] + ] + , object [ + leiaName + , "appearsIn" .= [ "NEWHOPE","EMPIRE","JEDI" :: Text] + , "friends" .= [ + object [lukeName] + , object [hanName] + , object [c3poName] + , object [r2d2Name] + ] + ] + ] + ] + ]] + it "Luke ID" $ testQuery + [r| query FetchLukeQuery { + human(id: "1000") { + name + } + } + |] + $ object [ "data" .= object [ + "human" .= object [lukeName] + ]] + + it "Luke ID with variable" $ testQueryParams + (\v -> if v == "someId" then Just "1000" else Nothing) + [r| query FetchSomeIDQuery($someId: String!) { + human(id: $someId) { + name + } + } + |] + $ object [ "data" .= object [ + "human" .= object [lukeName] + ]] + it "Han ID with variable" $ testQueryParams + (\v -> if v == "someId" then Just "1002" else Nothing) + [r| query FetchSomeIDQuery($someId: String!) { + human(id: $someId) { + name + } + } + |] + $ object [ "data" .= object [ + "human" .= object [hanName] + ]] + it "Invalid ID" $ testQueryParams + (\v -> if v == "id" then Just "Not a valid ID" else Nothing) + [r| query humanQuery($id: String!) { + human(id: $id) { + name + } + } + |] $ object ["data" .= object ["human" .= Aeson.Null]] + it "Luke aliased" $ testQuery + [r| query FetchLukeAliased { + luke: human(id: "1000") { + name + } + } + |] + $ object [ "data" .= object [ + "luke" .= object [lukeName] + ]] + it "R2-D2 ID and friends aliased" $ testQuery + [r| query HeroNameAndFriendsQuery { + hero { + id + name + friends { + friendName: name + } + } + } + |] + $ object [ "data" .= object [ + "hero" .= object [ + "id" .= ("2001" :: Text) + , r2d2Name + , "friends" .= [ + object ["friendName" .= ("Luke Skywalker" :: Text)] + , object ["friendName" .= ("Han Solo" :: Text)] + , object ["friendName" .= ("Leia Organa" :: Text)] + ] + ] + ]] + it "Luke and Leia aliased" $ testQuery + [r| query FetchLukeAndLeiaAliased { + luke: human(id: "1000") { + name + } + leia: human(id: "1003") { + name + } + } + |] + $ object [ "data" .= object [ + "luke" .= object [lukeName] + , "leia" .= object [leiaName] + ]] + + describe "Fragments for complex queries" $ do + it "Aliases to query for duplicate content" $ testQuery + [r| query DuplicateFields { + luke: human(id: "1000") { + name + homePlanet + } + leia: human(id: "1003") { + name + homePlanet + } + } + |] + $ object [ "data" .= object [ + "luke" .= object [lukeName, tatooine] + , "leia" .= object [leiaName, alderaan] + ]] + it "Fragment for duplicate content" $ testQuery + [r| query UseFragment { + luke: human(id: "1000") { + ...HumanFragment + } + leia: human(id: "1003") { + ...HumanFragment + } + } + fragment HumanFragment on Human { + name + homePlanet + } + |] + $ object [ "data" .= object [ + "luke" .= object [lukeName, tatooine] + , "leia" .= object [leiaName, alderaan] + ]] + + describe "__typename" $ do + it "R2D2 is a Droid" $ testQuery + [r| query CheckTypeOfR2 { + hero { + __typename + name + } + } + |] + $ object ["data" .= object [ + "hero" .= object ["__typename" .= ("Droid" :: Text), r2d2Name] + ]] + it "Luke is a human" $ testQuery + [r| query CheckTypeOfLuke { + hero(episode: EMPIRE) { + __typename + name + } + } + |] + $ object ["data" .= object [ + "hero" .= object ["__typename" .= ("Human" :: Text), lukeName] + ]] + + describe "Errors in resolvers" $ do + it "error on secretBackstory" $ testQuery + [r| + query HeroNameQuery { + hero { + name + secretBackstory + } + } + |] + $ object + [ "data" .= object + [ "hero" .= object + [ "name" .= ("R2-D2" :: Text) + , "secretBackstory" .= Aeson.Null + ] + ] + , "errors" .= + [ object + ["message" .= ("secretBackstory is secret." :: Text)] + ] + ] + it "Error in a list" $ testQuery + [r| query HeroNameQuery { + hero { + name + friends { + name + secretBackstory + } + } + } + |] + $ object ["data" .= object + [ "hero" .= object + [ "name" .= ("R2-D2" :: Text) + , "friends" .= + [ object + [ "name" .= ("Luke Skywalker" :: Text) + , "secretBackstory" .= Aeson.Null + ] + , object + [ "name" .= ("Han Solo" :: Text) + , "secretBackstory" .= Aeson.Null + ] + , object + [ "name" .= ("Leia Organa" :: Text) + , "secretBackstory" .= Aeson.Null + ] + ] + ] + ] + , "errors" .= + [ object ["message" .= ("secretBackstory is secret." :: Text)] + , object ["message" .= ("secretBackstory is secret." :: Text)] + , object ["message" .= ("secretBackstory is secret." :: Text)] + ] + ] + it "error on secretBackstory with alias" $ testQuery + [r| query HeroNameQuery { + mainHero: hero { + name + story: secretBackstory + } + } + |] + $ object + [ "data" .= object + [ "mainHero" .= object + [ "name" .= ("R2-D2" :: Text) + , "story" .= Aeson.Null + ] + ] + , "errors" .= + [ object ["message" .= ("secretBackstory is secret." :: Text)] + ] + ] + + where + lukeName = "name" .= ("Luke Skywalker" :: Text) + leiaName = "name" .= ("Leia Organa" :: Text) + hanName = "name" .= ("Han Solo" :: Text) + r2d2Name = "name" .= ("R2-D2" :: Text) + c3poName = "name" .= ("C-3PO" :: Text) + tatooine = "homePlanet" .= ("Tatooine" :: Text) + alderaan = "homePlanet" .= ("Alderaan" :: Text) + +testQuery :: Text -> Aeson.Value -> Expectation +testQuery q expected = graphql schema q >>= flip shouldBe expected + +testQueryParams :: Subs -> Text -> Aeson.Value -> Expectation +testQueryParams f q expected = graphqlSubs schema f q >>= flip shouldBe expected -- cgit v1.2.3