packages feed

greskell-core-1.0.0.1: test/Data/Greskell/GMapSpec.hs

{-# LANGUAGE OverloadedStrings #-}
module Data.Greskell.GMapSpec
    ( main
    , spec
    ) where

import           Data.Aeson             (Value (..), eitherDecode, object, toJSON, (.=))
import qualified Data.Aeson.KeyMap      as KM
import           Data.HashMap.Strict    (HashMap)
import qualified Data.HashMap.Strict    as HM
import           Data.List              (isInfixOf)
import           Data.Monoid            (mempty)
import           Data.Vector            (Vector, (!))
import qualified Data.Vector            as Vec
import           Test.Hspec

import           Data.Greskell.GMap     (FlattenedMap, GMap (..), GMapEntry (..))
import           Data.Greskell.GraphSON (GraphSON (..), nonTypedGraphSON, typedGraphSON)

main :: IO ()
main = hspec spec

spec :: Spec
spec = do
  spec_FlattenedMap
  spec_GMap
  spec_GMapEntry

spec_FlattenedMap :: Spec
spec_FlattenedMap = describe "FlattenedMap" $ do
  specify "decode an array with odd number of elements" $ do
    let got :: Either String (FlattenedMap HashMap Int String)
        got = eitherDecode "[10, \"ten\", 11]"
    case got of
      Right _  -> expectationFailure ("should be Left, but got " ++ show got)
      Left err -> err `shouldContain` "odd number of elements"

spec_GMap :: Spec
spec_GMap = describe "GraphSON GMap" $ do
  describe "non-flat" $ do
    let val :: GraphSON (GMap HashMap String Int)
        val = nonTypedGraphSON $ GMap False $ HM.fromList [("foo", 3), ("bar", 5), ("a", 1)]
    specify "FromJSON" $ do
      let input = "{\"foo\":3, \"a\": 1, \"bar\": 5}"
      eitherDecode input `shouldBe` Right val
    specify "ToJSON" $ do
      let expected = object [ "foo" .= Number 3,
                              "bar" .= Number 5,
                              "a"   .= Number 1
                            ]
      toJSON val `shouldBe` expected
    specify "FromJSON empty" $ do
      let val_empty :: GraphSON (GMap HashMap String Int)
          val_empty = nonTypedGraphSON $ GMap False mempty
      eitherDecode "{}" `shouldBe` Right val_empty
  describe "flat" $ do
    let val :: GraphSON (GMap HashMap String Int)
        val = typedGraphSON $ GMap True $ HM.fromList [("foo", 3), ("bar", 5), ("a", 1)]
    specify "FromJSON" $ do
      let input = "{\"@type\": \"g:Map\", \"@value\": [\"a\", 1, \"bar\", 5, \"foo\", 3]}"
      eitherDecode input `shouldBe` Right val
    specify "ToJSON" $ do
      let exp_flat = Vec.fromList [ String "foo", Number 3,
                                    String "bar", Number 5,
                                    String "a",   Number 1
                                  ]
          (Object got) = toJSON val
      KM.lookup "@type" got `shouldBe` (Just $ String "g:Map")
      let (Just (Array got_flat)) = KM.lookup "@value" got
      pairList got_flat `shouldMatchList` pairList exp_flat
    specify "FromJSON empty" $ do
      let val_empty :: GraphSON (GMap HashMap String Int)
          val_empty = typedGraphSON $ GMap True mempty
      eitherDecode "{\"@type\": \"g:Map\", \"@value\": []}" `shouldBe` Right val_empty

pairList :: Vector Value -> [(Value,Value)]
pairList a = map toPair $ [0 .. imax]
  where
    imax = (Vec.length a `div` 2) - 1
    toPair i = (a ! (i*2), a ! (i*2 + 1))

expLeft :: Show a => Either String a -> (String -> Bool) -> IO ()
expLeft e@(Right _) _ = expectationFailure ("expects Left, but got " ++ show e)
expLeft (Left e)    p = e `shouldSatisfy` p

spec_GMapEntry :: Spec
spec_GMapEntry = describe "GMapEntry" $ do
  specify "zero entry" $ do
    let got :: Either String (GMapEntry String Int)
        got = eitherDecode "[]"
    got `expLeft` ("0 entries" `isInfixOf`)
  specify "two entries" $ do
    let got :: Either String (GMapEntry String Int)
        got = eitherDecode "{\"foo\": 10, \"bar\": 20}"
    got `expLeft` ("Unexpected structure" `isInfixOf`)