packages feed

greskell-2.0.3.5: test/Data/Greskell/BinderSpec.hs

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

import           Control.Applicative    ((<$>), (<*>))
import           Control.Monad          (forM_)
import           Data.Aeson             (toJSON)
import qualified Data.Aeson.Key         as Key
import qualified Data.Aeson.KeyMap      as KM
import           Data.Text              (Text)
import qualified Data.Text              as T
import           Test.Hspec

import           Data.Greskell.AsLabel  (AsLabel)
import           Data.Greskell.Binder   (Binder, newAsLabel, newBind, runBinder)
import           Data.Greskell.Greskell (Greskell, toGremlin, unsafeGreskell)

main :: IO ()
main = hspec spec

extractVarName :: Greskell a -> IO Text
extractVarName got_greskell = checkVarName =<< (stripParens $ toGremlin got_greskell)
  where
    stripParens v =
      case T.stripPrefix "((" =<< T.stripSuffix "))" v of
        Nothing -> do
          expectationFailure "Binder should produce an expression of a variable wrapped with double parens"
          return ""
        Just a  -> return a
    checkVarName v =
      case T.unpack v of
        [] -> do
          expectationFailure "Expect a Gremlin variable, but got empty script."
          return ""
        (h : rest) -> do
          h `shouldSatisfy` (`elem` variableHeads)
          forM_ rest (`shouldSatisfy` (`elem` variableRests))
          return v
    variableHeads = '_' : (['a' .. 'z'] ++ ['A' .. 'Z'])
    variableRests = variableHeads ++ ['0' .. '9']

spec :: Spec
spec = describe "Binder" $ do
  it "should keep bound values" $ do
    let b = do
          v1 <- newBind (100 :: Int)
          v2 <- newBind ("hogehoge" :: Text)
          return (v1, v2)
        ((got_v1, got_v2), got_bind) = runBinder b
    toGremlin got_v1 `shouldNotBe` toGremlin got_v2
    v1Name <- extractVarName got_v1
    v2Name <- extractVarName got_v2
    got_bind `shouldBe` KM.fromList [ (Key.fromText v1Name, toJSON (100 :: Int)),
                                      (Key.fromText v2Name, toJSON ("hogehoge" :: Text))
                                    ]
  it "should compose and produce new variables" $ do
    let b = newBind ("foobar" :: Text)
        ((got_v1, got_v2), got_bind) = runBinder $ ((,) <$> b <*> b)
    toGremlin got_v1 `shouldNotBe` toGremlin got_v2
    v1Name <- extractVarName got_v1
    v2Name <- extractVarName got_v2
    got_bind `shouldBe` KM.fromList [ (Key.fromText v1Name, toJSON ("foobar" :: Text)),
                                      (Key.fromText v2Name, toJSON ("foobar" :: Text))
                                    ]
  it "should also be able to produce AsLabels" $ do
    let newIntLabel :: Binder (AsLabel Int)
        newIntLabel = newAsLabel
        newVar = newBind ("foobar" :: Text)
        ((got_v1, got_l1, got_v2, got_l2), _) =
          runBinder $ ((,,,) <$> newVar <*> newIntLabel <*> newVar <*> newIntLabel)
    _ <- extractVarName got_v1
    _ <- extractVarName got_v2
    toGremlin got_v1 `shouldNotBe` toGremlin got_v2
    got_l1 `shouldNotBe` got_l2