module ValueTests (tests) where

import Protolude

import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck (forAll)
import Test.Tasty (TestTree)
import Test.Tasty.Hspec (testSpec, describe, it, shouldBe, shouldSatisfy)

import qualified GraphQL.Internal.Syntax.AST as AST
import GraphQL.Internal.Arbitrary (arbitraryText, arbitraryNonEmpty)
import GraphQL.Value
  ( Object
  , ObjectField'(..)
  , astToVariableValue
  , unionObjects
  , objectFields
  , objectFromList
  )
import GraphQL.Value.FromValue (prop_roundtripValue)
import GraphQL.Value.ToValue (toValue)


tests :: IO TestTree
tests = testSpec "Value" $ do
  describe "unionObject" $ do
    it "returns empty on empty list" $ do
      unionObjects [] `shouldBe` (objectFromList [] :: Maybe Object)
    it "merges objects" $ do
      let (Just foo) = objectFromList [ ("foo", toValue @Int32 1)
                                      , ("bar",toValue @Int32 2)]
      let (Just bar) = objectFromList [ ("bar", toValue @Text "cow")
                                      , ("baz",toValue @Int32 3)]
      let observed = unionObjects [foo, bar]
      observed `shouldBe` Nothing
    it "merges objects with unique keys" $ do
      let (Just foo) = objectFromList [("foo", toValue @Int32 1)]
      let (Just bar) = objectFromList [ ("bar", toValue @Text "cow")
                                      , ("baz",toValue @Int32 3)]
      let (Just expected) = objectFromList [ ("foo", toValue @Int32 1)
                                           , ("bar", toValue @Text "cow")
                                           , ("baz", toValue @Int32 3)
                                           ]
      let (Just observed) = unionObjects [foo, bar]
      observed `shouldBe` expected
      expected `shouldSatisfy` prop_fieldsUnique
  describe "Objects" $ do
    prop "have unique fields" $ do
      prop_fieldsUnique
  describe "ToValue / FromValue instances" $ do
    prop "Bool" $ prop_roundtripValue @Bool
    prop "Int32" $ prop_roundtripValue @Int32
    prop "Double" $ prop_roundtripValue @Double
    prop "Text" $ forAll arbitraryText prop_roundtripValue
    prop "Lists" $ prop_roundtripValue @[Int32]
    prop "Non-empty lists" $ forAll (arbitraryNonEmpty @Int32) prop_roundtripValue
  describe "AST" $ do
    it "Objects converted from AST have unique fields" $ do
      let input = AST.ObjectValue [ AST.ObjectField "foo" (AST.ValueString (AST.StringValue "bar"))
                                  , AST.ObjectField "foo" (AST.ValueString (AST.StringValue "qux"))
                                  ]
      astToVariableValue (AST.ValueObject input) `shouldBe` Nothing


-- | All of the fields in an object should have unique names.
prop_fieldsUnique :: Object -> Bool
prop_fieldsUnique object =
  fieldNames == ordNub fieldNames
  where
    fieldNames = [name | ObjectField name _ <- objectFields object]