packages feed

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

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

import Data.Aeson (eitherDecode, object, (.=), toJSON, Value(..))
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.GraphSON (GraphSON(..), typedGraphSON, nonTypedGraphSON)
import Data.Greskell.GMap (GMap(..), GMapEntry(..))

main :: IO ()
main = hspec spec

spec :: Spec
spec = do
  spec_GMap
  spec_GMapEntry

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
      HM.lookup "@type" got `shouldBe` (Just $ String "g:Map")
      let (Just (Array got_flat)) = HM.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`)