{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FlexibleInstances, TemplateHaskell #-} module Main where import Control.Monad (void, join) import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Monoid (Monoid, mappend) import Data.Int (Int64) import Data.Maybe (fromJust) import qualified Data.Aeson as J import qualified Data.ByteString as S import qualified Data.HashMap.Lazy as M import qualified Data.HashSet as HS import qualified Data.Text as T import qualified Data.Text.Encoding as TE import qualified Data.Vector as V import Control.Exception (handle) import Test.Framework.TH (defaultMainGenerator) import Test.Framework.Providers.HUnit (testCase) import Test.HUnit.Base hiding (Test, Node) import Database.Neo4j import qualified Database.Neo4j.Batch as B import qualified Database.Neo4j.Cypher as C import qualified Database.Neo4j.Graph as G import qualified Database.Neo4j.Transactional.Cypher as TC (<>) :: Monoid a => a -> a -> a (<>) = mappend -- Get the string of a exception raised by an action getException :: IO a -> IO (Maybe Neo4jException) getException action = handle (return . Just) $ do _ <- action return Nothing assertException :: Neo4jException -> IO a -> Assertion assertException exExc action = do resExc <- getException action checkExc resExc where --checkExc :: (Exception e, Show e) => Maybe e -> Assertion checkExc Nothing = assertFailure $ "Expected exception " <> show exExc <> " but none raised" checkExc (Just e) = assertEqual "" exExc e -- | handy assertEqual inside a Neo4j monad neo4jEqual :: (Show a, Eq a) => a -> a -> Neo4j () neo4jEqual a b = liftIO $ assertEqual "" a b -- | handy assertBool inside a Neo4j monad neo4jBool :: Bool -> Neo4j () neo4jBool f = liftIO $ assertBool "" f main :: IO () main = $(defaultMainGenerator) -- TODO: Is it possible to avoid so many type declarations for literals when constructing properties?? -- | Dummy properties someProperties :: Properties someProperties = M.fromList ["mytext" |: ("mytext" :: T.Text), "textarrayprop" |: ["a" :: T.Text, "", "adeu"], "int" |: (-12 :: Int64), "intarray" |: [1 :: Int64, 2], "double" |: (-12.23 :: Double), "doublearray" |: [0.1, -12.23 :: Double], "bool" |: False, "aboolproparray" |: [False, True] ] -- | Default Neo4j port port :: Port port = 7474 -- | Default Neo4j host host :: Hostname host = "localhost" someOtherProperties :: Properties someOtherProperties = M.fromList ["hola" |: ("adeu" :: T.Text), "proparray" |: ["a" :: T.Text, "", "adeu"]] anotherProperties :: Properties anotherProperties = M.empty myRelType :: T.Text myRelType = "MYREL" -- | Test connecting to a non-existing server case_NoConnection :: Assertion case_NoConnection = assertException expException $ withConnection "localhost" 77 $ createNode someProperties where expException = Neo4jHttpException "FailedConnectionException2 \"localhost\" 77 False connect: does\ \ not exist (Connection refused)" -- | Test get and create a node case_CreateGetDeleteNode :: Assertion case_CreateGetDeleteNode = withConnection host port $ do n <- createNode someProperties newN <- getNode n neo4jEqual (Just n) newN deleteNode n -- | Test delete and get a node case_CreateDeleteGetNode :: Assertion case_CreateDeleteGetNode = withConnection host port $ do n <- createNode someProperties deleteNode n newN <- getNode n neo4jEqual Nothing newN -- | Test double delete case_DoubleDeleteNode :: Assertion case_DoubleDeleteNode = withConnection host port $ do n <- createNode someProperties deleteNode n deleteNode n -- | Test get node by id case_GetNodeById :: Assertion case_GetNodeById = withConnection host port $ do n <- createNode someProperties newN <- getNode (nodeId n) neo4jEqual (Just n) newN deleteNode n -- | Test get node with unexisting id case_GetUnexistingNodeById :: Assertion case_GetUnexistingNodeById = withConnection host port $ do newN <- getNode ("unexistingnode" :: S.ByteString) neo4jEqual Nothing newN -- | Test delete node by id case_DeleteNodeById :: Assertion case_DeleteNodeById = withConnection host port $ do n <- createNode someProperties deleteNode (nodeId n) -- | Test delete unexisting node by id case_DeleteUnexistingNodeById :: Assertion case_DeleteUnexistingNodeById = withConnection host port $ deleteNode ("unexistingnode" :: S.ByteString) -- | Refresh node properties from the DB case_getNodeProperties :: Assertion case_getNodeProperties = withConnection host port $ do n <- createNode someProperties props <- getProperties n neo4jEqual someProperties props deleteNode n -- | Get node properties from a deleted node case_getDeletedNodeProperties :: Assertion case_getDeletedNodeProperties = do n <- withConnection host port $ createNode someProperties let expException = Neo4jNoEntityException $ runNodeIdentifier n assertException expException $ withConnection host port $ do deleteNode n getProperties n -- | Get a property from a node case_getNodeProperty :: Assertion case_getNodeProperty = withConnection host port $ do n <- createNode someProperties prop <- getProperty n "intarray" neo4jEqual (M.lookup "intarray" someProperties) prop deleteNode n -- | Get an unexisting property from a node case_getNodeUnexistingProperty :: Assertion case_getNodeUnexistingProperty = withConnection host port $ do n <- createNode someProperties prop <- getProperty n "noproperty" neo4jEqual Nothing prop deleteNode n -- | Get a property from an unexisting node case_getUnexistingNodeProperty :: Assertion case_getUnexistingNodeProperty = do n <- withConnection host port $ createNode someProperties let expException = Neo4jNoEntityException $ runNodeIdentifier n assertException expException $ withConnection host port $ do deleteNode n _ <- getProperty n "noproperty" return () -- | Get change the properties of a node case_changeNodeProperties :: Assertion case_changeNodeProperties = withConnection host port $ do n <- createNode someProperties newN <- setProperties n otherProperties neo4jEqual otherProperties (getNodeProperties newN) renewN <- getNode n neo4jEqual otherProperties (getNodeProperties $ fromJust renewN) deleteNode newN where otherProperties = M.insert "newbool" (newval True) someProperties -- | Get change the properties of a node that doesn't exist case_changeUnexistingNodeProperties :: Assertion case_changeUnexistingNodeProperties = do n <- withConnection host port $ createNode someProperties let expException = Neo4jNoEntityException $ runNodeIdentifier n assertException expException $ withConnection host port $ do deleteNode n setProperties n someProperties -- | Change a property of a node case_changeNodeProperty :: Assertion case_changeNodeProperty = withConnection host port $ do n <- createNode someProperties newN <- setProperty n otherValName otherVal neo4jEqual otherProperties (getNodeProperties newN) renewN <- getNode n neo4jEqual otherProperties (getNodeProperties $ fromJust renewN) deleteNode newN where otherProperties = M.insert otherValName otherVal someProperties otherValName = "int" otherVal = newval False -- | Change an array property of a node to empty case_changeNodePropertyToEmpty :: Assertion case_changeNodePropertyToEmpty = withConnection host port $ do n <- createNode someProperties newN <- setProperty n otherValName otherVal neo4jEqual otherProperties (getNodeProperties newN) renewN <- getNode n neo4jEqual otherProperties (getNodeProperties $ fromJust renewN) deleteNode newN where otherProperties = M.insert otherValName otherVal someProperties otherValName = "intarray" otherVal = newval ([] :: [Int64]) -- | Set an unexisting property of a node case_changeNodeUnexistingProperty :: Assertion case_changeNodeUnexistingProperty = withConnection host port $ do n <- createNode someProperties newN <- setProperty n otherValName otherVal neo4jEqual otherProperties (getNodeProperties newN) renewN <- getNode n neo4jEqual otherProperties (getNodeProperties $ fromJust renewN) deleteNode newN where otherProperties = M.insert otherValName otherVal someProperties otherValName = "mynewbool" otherVal = newval False -- | Set property of an unexisting node case_changeUnexistingNodeProperty :: Assertion case_changeUnexistingNodeProperty = do n <- withConnection host port $ createNode someProperties let expException = Neo4jNoEntityException $ runNodeIdentifier n assertException expException $ withConnection host port $ do deleteNode n _ <- setProperty n otherValName otherVal return () where otherValName = "mynewbool" otherVal = newval False -- | Delete node properties case_deleteNodeProperties :: Assertion case_deleteNodeProperties = withConnection host port $ do n <- createNode someProperties newN <- deleteProperties n neo4jEqual M.empty (getNodeProperties newN) renewN <- getNode n neo4jEqual M.empty (getNodeProperties $ fromJust renewN) deleteNode newN -- | Delete unexisting node properties case_deleteUnexistingNodeProperties :: Assertion case_deleteUnexistingNodeProperties = do n <- withConnection host port $ createNode someProperties let expException = Neo4jNoEntityException $ runNodeIdentifier n assertException expException $ withConnection host port $ do deleteNode n _ <- deleteProperties n return () -- | Delete a node property case_deleteNodeProperty :: Assertion case_deleteNodeProperty = withConnection host port $ do n <- createNode someProperties newN <- deleteProperty n valName neo4jEqual otherProperties (getNodeProperties newN) renewN <- getNode n neo4jEqual otherProperties (getNodeProperties $ fromJust renewN) deleteNode newN where otherProperties = M.delete valName someProperties valName = "int" -- | Delete an unexisting property from a node case_deleteNodeUnexistingProperty :: Assertion case_deleteNodeUnexistingProperty = withConnection host port $ do n <- createNode someProperties newN <- deleteProperty n valName neo4jEqual otherProperties (getNodeProperties newN) renewN <- getNode n neo4jEqual otherProperties (getNodeProperties $ fromJust renewN) deleteNode newN where otherProperties = M.delete valName someProperties valName = "noproperty" -- | Delete a property from an unexisting node case_deleteUnexistingNodeProperty :: Assertion case_deleteUnexistingNodeProperty = do n <- withConnection host port $ createNode someProperties let expException = Neo4jNoEntityException $ runNodeIdentifier n assertException expException $ withConnection host port $ do deleteNode n _ <- deleteProperty n valName return () where valName = "noproperty" setupRelTests :: Neo4j (Node, Node, Relationship) setupRelTests = do nodeFrom <- createNode anotherProperties nodeTo <- createNode someOtherProperties r <- createRelationship myRelType someProperties nodeFrom nodeTo return (nodeFrom, nodeTo, r) teardownRelTests :: Node -> Node -> Relationship -> Neo4j() teardownRelTests f t r = do deleteRelationship r deleteNode f deleteNode t -- | Delete a node with a relationship case_deleteNodeWithRelationship :: Assertion case_deleteNodeWithRelationship = do (nodeFrom, nodeTo, r) <- withConnection host port setupRelTests let expException = Neo4jNonOrphanNodeDeletionException $ runNodeIdentifier nodeFrom assertException expException $ withConnection host port $ deleteNode nodeFrom withConnection host port $ teardownRelTests nodeFrom nodeTo r -- | Test get and create a relationship case_CreateGetDeleteRelationship :: Assertion case_CreateGetDeleteRelationship = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests newN <- getRelationship r neo4jEqual (Just r) newN teardownRelTests nodeFrom nodeTo r -- | Test get all relationship properties case_allRelationshipProperties :: Assertion case_allRelationshipProperties = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests rs <- allRelationshipTypes neo4jBool $ myRelType `elem` rs teardownRelTests nodeFrom nodeTo r -- | Test get the start and end of a relationship case_relationshipFromTo :: Assertion case_relationshipFromTo = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests nfrom <- getRelationshipFrom r neo4jEqual nodeFrom nfrom nto <- getRelationshipTo r neo4jEqual nodeTo nto teardownRelTests nodeFrom nodeTo r -- | Test get the start of relationship that doesn't exist any more case_nonExistingRelationshipFrom :: Assertion case_nonExistingRelationshipFrom = do (nodeFrom, nodeTo, r) <- withConnection host port setupRelTests let expException = Neo4jNoEntityException $ runNodeIdentifier nodeFrom assertException expException $ withConnection host port $ do deleteRelationship r deleteNode nodeFrom n <- getRelationshipFrom r neo4jEqual nodeFrom n -- Doing this to force the evaluation of n (the exception is thrown after parsing) return () withConnection host port $ teardownRelTests nodeFrom nodeTo r -- | Test get the end of relationship that doesn't exist any more case_nonExistingRelationshipTo :: Assertion case_nonExistingRelationshipTo = do (nodeFrom, nodeTo, r) <- withConnection host port setupRelTests let expException = Neo4jNoEntityException $ runNodeIdentifier nodeTo assertException expException $ withConnection host port $ do deleteRelationship r deleteNode nodeTo n <- getRelationshipTo r neo4jEqual nodeTo n -- Doing this to force the evaluation of n (the exception is thrown after parsing) return () withConnection host port $ teardownRelTests nodeFrom nodeTo r -- | Create relationship with missing node from case_CreateRelationshipMissingFrom :: Assertion case_CreateRelationshipMissingFrom = do (nodeFrom, nodeTo) <- withConnection host port $ do nodeFrom <- createNode anotherProperties nodeTo <- createNode someOtherProperties return (nodeFrom, nodeTo) let expException = Neo4jNoEntityException $ runNodeIdentifier nodeFrom assertException expException $ withConnection host port $ do deleteNode nodeFrom _ <- createRelationship myRelType someProperties nodeFrom nodeTo return () withConnection host port $ deleteNode nodeTo -- | Create relationship with missing node to case_CreateRelationshipMissingTo :: Assertion case_CreateRelationshipMissingTo = do (nodeFrom, nodeTo) <- withConnection host port $ do nodeFrom <- createNode anotherProperties nodeTo <- createNode someOtherProperties return (nodeFrom, nodeTo) let expException = Neo4jNoEntityException $ runNodeIdentifier nodeTo assertException expException $ withConnection host port $ do deleteNode nodeTo _ <- createRelationship myRelType someProperties nodeFrom nodeTo return () withConnection host port $ deleteNode nodeFrom -- | Test delete and get a relationship case_CreateDeleteGetRelationship :: Assertion case_CreateDeleteGetRelationship = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests deleteRelationship r newN <- getRelationship r neo4jEqual Nothing newN teardownRelTests nodeFrom nodeTo r -- | Test double delete case_DoubleDeleteRelationship :: Assertion case_DoubleDeleteRelationship = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests deleteRelationship r deleteRelationship r teardownRelTests nodeFrom nodeTo r -- | Test get relationship by id case_GetRelationshipById :: Assertion case_GetRelationshipById = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests newN <- getRelationship (relId r) neo4jEqual (Just r) newN teardownRelTests nodeFrom nodeTo r -- | Test get relationship with unexisting id case_GetUnexistingRelationshipById :: Assertion case_GetUnexistingRelationshipById = withConnection host port $ do newN <- getRelationship ("unexistingrelationship" :: S.ByteString) neo4jEqual Nothing newN -- | Test delete relationship by id case_DeleteRelationshipById :: Assertion case_DeleteRelationshipById = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests deleteRelationship (relId r) teardownRelTests nodeFrom nodeTo r -- | Test delete unexisting relationship by id case_DeleteUnexistingRelationshipById :: Assertion case_DeleteUnexistingRelationshipById = withConnection host port $ deleteRelationship ("unexistingrelationship" :: S.ByteString) -- | Refresh relationship properties from the DB case_getRelationshipProperties :: Assertion case_getRelationshipProperties = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests props <- getProperties r neo4jEqual someProperties props teardownRelTests nodeFrom nodeTo r -- | Get relationship properties from a deleted node case_getDeletedRelationshipProperties :: Assertion case_getDeletedRelationshipProperties = do (nodeFrom, nodeTo, r) <- withConnection host port setupRelTests let expException = Neo4jNoEntityException $ runRelIdentifier r assertException expException $ withConnection host port $ do deleteRelationship r deleteNode nodeFrom deleteNode nodeTo getProperties r -- | Get a property from a relationship case_getRelationshipProperty :: Assertion case_getRelationshipProperty = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests prop <- getProperty r "intarray" neo4jEqual (M.lookup "intarray" someProperties) prop teardownRelTests nodeFrom nodeTo r -- | Get an unexisting property from a relationship case_getRelationshipUnexistingProperty :: Assertion case_getRelationshipUnexistingProperty = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests prop <- getProperty r "noproperty" neo4jEqual Nothing prop teardownRelTests nodeFrom nodeTo r -- | Get a property from an unexisting relationship case_getUnexistingRelationshipProperty :: Assertion case_getUnexistingRelationshipProperty = do (nodeFrom, nodeTo, r) <- withConnection host port setupRelTests let expException = Neo4jNoEntityException $ runRelIdentifier r assertException expException $ withConnection host port $ do teardownRelTests nodeFrom nodeTo r _ <- getProperty r "noproperty" return () -- | Get change the properties of a relationship case_changeRelationshipProperties :: Assertion case_changeRelationshipProperties = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests newN <- setProperties r otherProperties neo4jEqual otherProperties (getRelProperties newN) renewN <- getRelationship r neo4jEqual otherProperties (getRelProperties $ fromJust renewN) teardownRelTests nodeFrom nodeTo newN where otherProperties = M.insert "newbool" (newval True) someProperties -- | Get change the properties of a relationship that doesn't exist case_changeUnexistingRelationshipProperties :: Assertion case_changeUnexistingRelationshipProperties = do (nodeFrom, nodeTo, r) <- withConnection host port setupRelTests let expException = Neo4jNoEntityException $ runRelIdentifier r assertException expException $ withConnection host port $ do deleteRelationship r deleteNode nodeFrom deleteNode nodeTo setProperties r someProperties -- | Change a property of a relationship case_changeRelationshipProperty :: Assertion case_changeRelationshipProperty = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests newN <- setProperty r otherValName otherVal neo4jEqual otherProperties (getRelProperties newN) renewN <- getRelationship r neo4jEqual otherProperties (getRelProperties $ fromJust renewN) teardownRelTests nodeFrom nodeTo newN where otherProperties = M.insert otherValName otherVal someProperties otherValName = "int" otherVal = newval False -- | Change an array property of a relationship to empty case_changeRelationshipPropertyToEmpty :: Assertion case_changeRelationshipPropertyToEmpty = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests newN <- setProperty r otherValName otherVal neo4jEqual otherProperties (getRelProperties newN) renewN <- getRelationship r neo4jEqual otherProperties (getRelProperties $ fromJust renewN) teardownRelTests nodeFrom nodeTo newN where otherProperties = M.insert otherValName otherVal someProperties otherValName = "intarray" otherVal = newval ([] :: [Int64]) -- | Set an unexisting property of a relationship case_changeRelationshipUnexistingProperty :: Assertion case_changeRelationshipUnexistingProperty = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests newN <- setProperty r otherValName otherVal neo4jEqual otherProperties (getRelProperties newN) renewN <- getRelationship r neo4jEqual otherProperties (getRelProperties $ fromJust renewN) teardownRelTests nodeFrom nodeTo newN where otherProperties = M.insert otherValName otherVal someProperties otherValName = "mynewbool" otherVal = newval False -- | Set property of an unexisting node case_changeUnexistingRelationshipProperty :: Assertion case_changeUnexistingRelationshipProperty = do (nodeFrom, nodeTo, r) <- withConnection host port setupRelTests let expException = Neo4jNoEntityException $ runRelIdentifier r assertException expException $ withConnection host port $ do deleteRelationship r _ <- setProperty r otherValName otherVal return () withConnection host port $ teardownRelTests nodeFrom nodeTo r where otherValName = "mynewbool" otherVal = newval False -- | Delete relationship properties case_deleteRelationshipProperties :: Assertion case_deleteRelationshipProperties = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests newN <- deleteProperties r neo4jEqual M.empty (getRelProperties newN) renewN <- getRelationship r neo4jEqual M.empty (getRelProperties $ fromJust renewN) teardownRelTests nodeFrom nodeTo newN -- | Delete unexisting relationship properties case_deleteUnexistingRelationshipProperties :: Assertion case_deleteUnexistingRelationshipProperties = do (nodeFrom, nodeTo, r) <- withConnection host port setupRelTests let expException = Neo4jNoEntityException $ runRelIdentifier r assertException expException $ withConnection host port $ do deleteRelationship r _ <- deleteProperties r return () withConnection host port $ teardownRelTests nodeFrom nodeTo r -- | Delete a relationship property case_deleteRelationshipProperty :: Assertion case_deleteRelationshipProperty = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests newN <- deleteProperty r valName neo4jEqual otherProperties (getRelProperties newN) renewN <- getRelationship r neo4jEqual otherProperties (getRelProperties $ fromJust renewN) teardownRelTests nodeFrom nodeTo newN where otherProperties = M.delete valName someProperties valName = "int" -- | Delete an unexisting property from a relationship case_deleteRelationshipUnexistingProperty :: Assertion case_deleteRelationshipUnexistingProperty = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests newN <- deleteProperty r valName neo4jEqual otherProperties (getRelProperties newN) renewN <- getRelationship r neo4jEqual otherProperties (getRelProperties $ fromJust renewN) teardownRelTests nodeFrom nodeTo newN where otherProperties = M.delete valName someProperties valName = "noproperty" -- | Delete a property from an unexisting relationship case_deleteUnexistingRelationshipProperty :: Assertion case_deleteUnexistingRelationshipProperty = do (nodeFrom, nodeTo, r) <- withConnection host port setupRelTests let expException = Neo4jNoEntityException $ runRelIdentifier r assertException expException $ withConnection host port $ do deleteRelationship r _ <- deleteProperty r valName return () withConnection host port $ teardownRelTests nodeFrom nodeTo r where valName = "noproperty" -- | Get relationships for a node case_GetNodeRelationships :: Assertion case_GetNodeRelationships = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests -- test getting relationships for the origin node rsAny <- getRelationships nodeFrom Any [] neo4jEqual [r] rsAny rsOutgoing <- getRelationships nodeFrom Outgoing [] neo4jEqual [r] rsOutgoing rsIncoming <- getRelationships nodeFrom Incoming [] neo4jEqual [] rsIncoming -- test getting relationships for the destination node rsAnyTo <- getRelationships nodeTo Any [] neo4jEqual [r] rsAnyTo rsOutgoingTo <- getRelationships nodeTo Outgoing [] neo4jEqual [] rsOutgoingTo rsIncomingTo <- getRelationships nodeTo Incoming [] neo4jEqual [r] rsIncomingTo teardownRelTests nodeFrom nodeTo r -- | Get relationships for an unexisting node case_GetUnexistingNodeRelationships :: Assertion case_GetUnexistingNodeRelationships = do (nodeFrom, nodeTo, r) <- withConnection host port setupRelTests let expException = Neo4jNoEntityException $ runNodeIdentifier nodeFrom assertException expException $ withConnection host port $ do teardownRelTests nodeFrom nodeTo r _ <- getRelationships nodeFrom Any [] return () -- | Get relationships for a node with multiple relationships case_GetNodeRelationshipsMultiple :: Assertion case_GetNodeRelationshipsMultiple = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests r2 <- createRelationship myRelType someOtherProperties nodeTo nodeFrom rsAny <- getRelationships nodeFrom Any [] neo4jBool (r `elem` rsAny) neo4jBool (r2 `elem` rsAny) neo4jEqual 2 (length rsAny) rsOutgoing <- getRelationships nodeFrom Outgoing [] neo4jEqual [r] rsOutgoing rsIncoming <- getRelationships nodeFrom Incoming [] neo4jEqual [r2] rsIncoming deleteRelationship r2 teardownRelTests nodeFrom nodeTo r -- | Get relationships for a node with a type filter case_GetNodeRelationshipsWithType :: Assertion case_GetNodeRelationshipsWithType = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests rsAny <- getRelationships nodeFrom Any [myRelType] neo4jEqual [r] rsAny teardownRelTests nodeFrom nodeTo r -- | Get relationships for a node with an unexisting type filter case_GetNodeRelationshipsWithUnexistingType :: Assertion case_GetNodeRelationshipsWithUnexistingType = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests rsAny <- getRelationships nodeFrom Any ["notype"] neo4jEqual [] rsAny teardownRelTests nodeFrom nodeTo r -- | Get relationships for a node with a type filter with multiple elements case_GetNodeRelationshipsWithTypes :: Assertion case_GetNodeRelationshipsWithTypes = withConnection host port $ do (nodeFrom, nodeTo, r) <- setupRelTests -- Create another relationship with another type r2 <- createRelationship type2 anotherProperties nodeTo nodeFrom rsAny <- getRelationships nodeFrom Any [myRelType, type2, "notype"] neo4jBool (r `elem` rsAny) neo4jBool (r2 `elem` rsAny) neo4jEqual 2 (length rsAny) deleteRelationship r2 teardownRelTests nodeFrom nodeTo r where type2 = "reltype2" -- | Get all labels in the DB (We don't have control of all the labels the DB has) case_getLabels :: Assertion case_getLabels = withConnection host port $ do n <- createNode someProperties addLabels [lbl] n lbls <- allLabels neo4jBool $ lbl `elem` lbls deleteNode n where lbl = "label1" -- | Get labels for a node it doesn't have any case_getNodeLabelsWithNone :: Assertion case_getNodeLabelsWithNone = withConnection host port $ do n <- createNode someProperties lbls <- getLabels n neo4jEqual [] lbls deleteNode n -- | Get labels for an unexisting node case_getUnexistingNodeLabels :: Assertion case_getUnexistingNodeLabels = do n <- withConnection host port $ createNode someProperties let expException = Neo4jNoEntityException $ runNodeIdentifier n assertException expException $ withConnection host port $ do deleteNode n _ <- getLabels n return () -- | Add labels to a node and get them case_getAddAndGetNodeLabels :: Assertion case_getAddAndGetNodeLabels = withConnection host port $ do n <- createNode someProperties addLabels [lbl1, lbl2] n addLabels [lbl3] n lbls <- getLabels n neo4jEqual 3 (length lbls) neo4jBool $ all (`elem` lbls) [lbl1, lbl2, lbl3] deleteNode n where lbl1 = "mylabel1" lbl2 = "mylabel2" lbl3 = "mylabel3" -- | Add labels to an unexisting node case_addUnexistingNodeLabels :: Assertion case_addUnexistingNodeLabels = do n <- withConnection host port $ createNode someProperties let expException = Neo4jNoEntityException $ runNodeIdentifier n assertException expException $ withConnection host port $ do deleteNode n addLabels ["mylabel"] n -- | Change node labels case_changeNodeLabels :: Assertion case_changeNodeLabels = withConnection host port $ do n <- createNode someProperties addLabels [lbl1, lbl2] n changeLabels [lbl3] n lbls <- getLabels n neo4jEqual [lbl3] lbls deleteNode n where lbl1 = "mylabel1" lbl2 = "mylabel2" lbl3 = "otherlabel" -- | Change node labels to empty case_changeNodeLabelsToEmpty :: Assertion case_changeNodeLabelsToEmpty = withConnection host port $ do n <- createNode someProperties addLabels [lbl1, lbl2] n changeLabels [] n lbls <- getLabels n neo4jEqual [] lbls deleteNode n where lbl1 = "mylabel1" lbl2 = "mylabel2" -- | Change labels for an unexisting node case_changeUnexistingNodeLabels :: Assertion case_changeUnexistingNodeLabels = do n <- withConnection host port $ createNode someProperties let expException = Neo4jNoEntityException $ runNodeIdentifier n assertException expException $ withConnection host port $ do deleteNode n changeLabels ["mylabel"] n -- | Remove a label from a node case_removeNodeLabel :: Assertion case_removeNodeLabel = withConnection host port $ do n <- createNode someProperties addLabels [lbl1, lbl2] n removeLabel lbl1 n lbls <- getLabels n neo4jEqual [lbl2] lbls deleteNode n where lbl1 = "mylabel1" lbl2 = "mylabel2" -- | Remove an unexisting label from a node (nothing should happen) case_removeNodeUnexistingLabel :: Assertion case_removeNodeUnexistingLabel = withConnection host port $ do n <- createNode someProperties addLabels [lbl1] n removeLabel "nolabel" n lbls <- getLabels n neo4jEqual [lbl1] lbls deleteNode n where lbl1 = "mylabel1" -- | Remove label for an unexisting node case_removeUnexistingNodeLabel :: Assertion case_removeUnexistingNodeLabel = do n <- withConnection host port $ createNode someProperties let expException = Neo4jNoEntityException $ runNodeIdentifier n assertException expException $ withConnection host port $ do deleteNode n removeLabel "mylabel" n -- | Get all nodes with a label case_allNodesWithLabel :: Assertion case_allNodesWithLabel = withConnection host port $ do n1 <- createNode someProperties n2 <- createNode someProperties n3 <- createNode someProperties addLabels [lbl1, lbl2] n1 addLabels [lbl2] n2 addLabels [lbl1] n3 ns <- getNodesByLabelAndProperty lbl1 Nothing neo4jEqual 2 (length ns) neo4jBool $ all (`elem` ns) [n1, n3] ns2 <- getNodesByLabelAndProperty "nolbl" Nothing neo4jEqual [] ns2 mapM_ deleteNode [n1, n2, n3] where lbl1 = "lbl1" lbl2 = "lbl2" -- | Get all nodes with a label and a property case_allNodesWithLabelAndProperty :: Assertion case_allNodesWithLabelAndProperty = withConnection host port $ do n1 <- createNode someProperties n2 <- createNode someProperties n3 <- createNode anotherProperties addLabels [lbl1, lbl2] n1 addLabels [lbl2] n2 addLabels [lbl1] n3 ns <- getNodesByLabelAndProperty lbl1 $ Just ("doublearray", newval [0.1, -12.23 :: Double]) neo4jEqual [n1] ns ns2 <- getNodesByLabelAndProperty lbl1 $ Just ("doublearray", newval [0.1, -12.22 :: Double]) neo4jEqual [] ns2 mapM_ deleteNode [n1, n2, n3] where lbl1 = "lbl1" lbl2 = "lbl2" -- | Create, get and destroy index case_createGetDropIndex :: Assertion case_createGetDropIndex = withConnection host port $ do dropIndex lbl prop1 dropIndex lbl prop2 dropIndex lbl2 prop1 dropIndex lbl2 prop2 idx1 <- createIndex lbl prop1 idx2 <- createIndex lbl prop2 idx3 <- createIndex lbl2 prop1 idx4 <- createIndex lbl2 prop2 idxs <- getIndexes lbl neo4jBool $ all (`elem` idxs) [idx1, idx2] neo4jBool $ all (not . (`elem` idxs)) [idx3, idx4] idxs2 <- getIndexes lbl2 neo4jBool $ all (not . (`elem` idxs2)) [idx1, idx2] neo4jBool $ all (`elem` idxs2) [idx3, idx4] dropIndex lbl prop1 dropIndex lbl prop2 dropIndex lbl2 prop1 dropIndex lbl2 prop2 idxs3 <- getIndexes lbl idxs4 <- getIndexes lbl2 neo4jBool $ all (not . (`elem` idxs3)) [idx1, idx2] neo4jBool $ all (not . (`elem` idxs4)) [idx3, idx4] where lbl = "mylabel11" lbl2 = "mylabel2" prop1 = "myprop" prop2 = "myprop2" -- | Test batch, create a node and get it again case_batchCreateGetNode :: Assertion case_batchCreateGetNode = withConnection host port $ do g <- B.runBatch $ do n <- B.createNode someProperties B.getNode n neo4jEqual 1 (length $ G.getNodes g) neo4jBool $ someProperties `elem` map getNodeProperties (G.getNodes g) deleteNode (head $ G.getNodes g) -- | Test batch, create two nodes in a batch case_batchCreate2Nodes :: Assertion case_batchCreate2Nodes = withConnection host port $ do g <- B.runBatch $ do _ <- B.createNode someProperties B.createNode anotherProperties neo4jEqual 2 (length $ G.getNodes g) neo4jBool $ someProperties `elem` map getNodeProperties (G.getNodes g) neo4jBool $ anotherProperties `elem` map getNodeProperties (G.getNodes g) mapM_ deleteNode (G.getNodes g) -- | Test batch, create and delete case_batchCreateDeleteNode :: Assertion case_batchCreateDeleteNode = withConnection host port $ do g <- B.runBatch $ do n <- B.createNode someProperties B.deleteNode n neo4jEqual 0 (length $ G.getNodes g) -- | Test batch, create two nodes and two relationships between them case_batchCreateRelationships :: Assertion case_batchCreateRelationships = withConnection host port $ do g <- B.runBatch $ do n1 <- B.createNode someProperties n2 <- B.createNode anotherProperties _ <- B.createRelationship "type1" someOtherProperties n1 n2 B.createRelationship "type2" someProperties n2 n1 neo4jEqual 2 (length $ G.getNodes g) neo4jEqual 2 (length $ G.getRelationships g) neo4jBool $ someOtherProperties `elem` map getRelProperties (G.getRelationships g) neo4jBool $ someProperties `elem` map getRelProperties (G.getRelationships g) neo4jBool $ "type1" `elem` map getRelType (G.getRelationships g) neo4jBool $ "type2" `elem` map getRelType (G.getRelationships g) let r1 : r2 : [] = G.getRelationships g neo4jEqual (G.getRelationshipNodeFrom r1 g) (G.getRelationshipNodeTo r2 g) neo4jEqual (G.getRelationshipNodeFrom r2 g) (G.getRelationshipNodeTo r1 g) _ <- B.runBatch $ do mapM_ B.deleteRelationship (G.getRelationships g) mapM_ B.deleteNode (G.getNodes g) return () -- | Test batch, create and delete a relationship case_batchCreateDelRelationships :: Assertion case_batchCreateDelRelationships = withConnection host port $ do g <- B.runBatch $ do n1 <- B.createNode someProperties n2 <- B.createNode anotherProperties r <- B.createRelationship "type1" someOtherProperties n1 n2 B.deleteRelationship r neo4jEqual 2 (length $ G.getNodes g) neo4jEqual 0 (length $ G.getRelationships g) mapM_ deleteRelationship (G.getRelationships g) mapM_ deleteNode (G.getNodes g) -- | Test batch getRelationshipFrom case_batchRelationshipNodeFrom :: Assertion case_batchRelationshipNodeFrom = withConnection host port $ do g <- B.runBatch $ do n1 <- B.createNode someProperties n2 <- B.createNode anotherProperties B.createRelationship "type1" someOtherProperties n1 n2 g2 <- B.runBatch $ B.getRelationshipFrom (head $ G.getRelationships g) neo4jEqual 1 (length $ G.getNodes g2) neo4jEqual 0 (length $ G.getRelationships g2) neo4jBool $ someProperties `elem` map getNodeProperties (G.getNodes g2) _ <- B.runBatch $ do mapM_ B.deleteRelationship (G.getRelationships g) mapM_ B.deleteNode (G.getNodes g) return () -- | Test batch getRelationshipTo case_batchRelationshipNodeTo :: Assertion case_batchRelationshipNodeTo = withConnection host port $ do g <- B.runBatch $ do n1 <- B.createNode someProperties n2 <- B.createNode anotherProperties B.createRelationship "type1" someOtherProperties n1 n2 g2 <- B.runBatch $ B.getRelationshipTo (head $ G.getRelationships g) neo4jEqual 1 (length $ G.getNodes g2) neo4jEqual 0 (length $ G.getRelationships g2) neo4jBool $ anotherProperties `elem` map getNodeProperties (G.getNodes g2) _ <- B.runBatch $ do mapM_ B.deleteRelationship (G.getRelationships g) mapM_ B.deleteNode (G.getNodes g) return () -- | Test batch, get all relationships with filter case_batchGetRelationships :: Assertion case_batchGetRelationships = withConnection host port $ do g <- B.runBatch $ do n1 <- B.createNode someProperties n2 <- B.createNode anotherProperties _ <- B.createRelationship "type1" someOtherProperties n1 n2 B.createRelationship "type2" someProperties n2 n1 g2 <- B.runBatch $ do let n = head $ G.getNodes g B.getRelationships n Any ["type1", "type2"] neo4jEqual 2 (length $ G.getRelationships g2) neo4jBool $ someOtherProperties `elem` map getRelProperties (G.getRelationships g2) neo4jBool $ someProperties `elem` map getRelProperties (G.getRelationships g2) neo4jBool $ "type1" `elem` map getRelType (G.getRelationships g2) neo4jBool $ "type2" `elem` map getRelType (G.getRelationships g2) mapM_ deleteRelationship (G.getRelationships g) mapM_ deleteNode (G.getNodes g) -- | Test batch, set properties case_batchSetProperties :: Assertion case_batchSetProperties = withConnection host port $ do let newProperties = M.fromList ["croqueta" |: ("2" :: T.Text)] g <- B.runBatch $ do n1 <- B.createNode someProperties n2 <- B.createNode anotherProperties r <- B.createRelationship "type1" someOtherProperties n1 n2 _ <- B.setProperties n1 newProperties B.setProperties r newProperties neo4jEqual 2 (length $ G.getNodes g) neo4jEqual 1 (length $ G.getRelationships g) neo4jBool $ newProperties `elem` map getRelProperties (G.getRelationships g) neo4jBool $ newProperties `elem` map getNodeProperties (G.getNodes g) _ <- B.runBatch $ do mapM_ B.deleteRelationship (G.getRelationships g) mapM_ B.deleteNode (G.getNodes g) return () -- | Test batch, set property case_batchSetProperty :: Assertion case_batchSetProperty = withConnection host port $ do let key = "hola" let val = newval False let newSomeProperties = M.insert key val someProperties let newSomeOtherProperties = M.insert key val someOtherProperties g <- B.runBatch $ do n1 <- B.createNode someProperties n2 <- B.createNode anotherProperties r <- B.createRelationship "type1" someOtherProperties n1 n2 _ <- B.setProperty n1 key val B.setProperty r key val neo4jEqual 2 (length $ G.getNodes g) neo4jEqual 1 (length $ G.getRelationships g) neo4jBool $ newSomeProperties `elem` map getNodeProperties (G.getNodes g) neo4jBool $ newSomeOtherProperties `elem` map getRelProperties (G.getRelationships g) _ <- B.runBatch $ do mapM_ B.deleteRelationship (G.getRelationships g) mapM_ B.deleteNode (G.getNodes g) return () -- | Test batch, delete properties case_batchDeleteProperties :: Assertion case_batchDeleteProperties = withConnection host port $ do g <- B.runBatch $ do n1 <- B.createNode someProperties n2 <- B.createNode anotherProperties r <- B.createRelationship "type1" someOtherProperties n1 n2 _ <- B.deleteProperties n1 B.deleteProperties r neo4jEqual 2 (length $ G.getNodes g) neo4jEqual 1 (length $ G.getRelationships g) neo4jBool $ emptyProperties `elem` map getRelProperties (G.getRelationships g) neo4jBool $ emptyProperties `elem` map getNodeProperties (G.getNodes g) _ <- B.runBatch $ do mapM_ B.deleteRelationship (G.getRelationships g) mapM_ B.deleteNode (G.getNodes g) return () -- | Test batch, delete property case_batchDeleteProperty :: Assertion case_batchDeleteProperty= withConnection host port $ do let key = "mytext" let newSomeProperties = M.delete key someProperties g <- B.runBatch $ do n1 <- B.createNode someProperties n2 <- B.createNode anotherProperties r <- B.createRelationship "type1" someProperties n1 n2 _ <- B.deleteProperty n1 key B.deleteProperty r key neo4jEqual 2 (length $ G.getNodes g) neo4jEqual 1 (length $ G.getRelationships g) neo4jBool $ newSomeProperties `elem` map getNodeProperties (G.getNodes g) neo4jBool $ newSomeProperties `elem` map getRelProperties (G.getRelationships g) _ <- B.runBatch $ do mapM_ B.deleteRelationship (G.getRelationships g) mapM_ B.deleteNode (G.getNodes g) return () -- | Test batch, delete an unknown property, it should raise an exception case_batchDeleteUnknownProperty :: Assertion case_batchDeleteUnknownProperty= do n <- withConnection host port $ createNode someProperties let excMsg = "Node[" <> TE.decodeUtf8 (nodeId n) <>"] does not have a property \"" <> prop <> "\"" let expException = Neo4jNoSuchProperty excMsg assertException expException $ withConnection host port $ do g <- B.runBatch $ B.deleteProperty n prop neo4jEqual G.empty g -- Doing this to force evaluation withConnection host port $ deleteNode n where prop = "noprop" :: T.Text -- | Test batch, set add labels case_batchAddLabels :: Assertion case_batchAddLabels = withConnection host port $ do let label1 = "label1" let label2 = "label2" let label3 = "label3" let labelset = HS.fromList [label1, label2, label3] g <- B.runBatch $ do n1 <- B.createNode someProperties _ <- B.createNode anotherProperties _ <- B.addLabels [label1, label2] n1 B.addLabels [label3] n1 neo4jEqual 2 (length $ G.getNodes g) neo4jBool $ labelset `elem` map (`G.getNodeLabels` g) (G.getNodes g) neo4jBool $ HS.empty `elem` map (`G.getNodeLabels` g) (G.getNodes g) _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes g) return () -- | Test batch, change labels case_batchChangeLabels :: Assertion case_batchChangeLabels = withConnection host port $ do let label1 = "label1" let label2 = "label2" let label3 = "label3" let labelset = HS.fromList [label2] g <- B.runBatch $ do n1 <- B.createNode someProperties _ <- B.addLabels [label1, label2, label3] n1 B.changeLabels [label2] n1 neo4jBool $ labelset `elem` map (`G.getNodeLabels` g) (G.getNodes g) _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes g) return () -- | Test batch, remove label case_batchRemoveLabel :: Assertion case_batchRemoveLabel = withConnection host port $ do let label1 = "label1" let label2 = "label2" let label3 = "label3" let labelset = HS.fromList [label2, label3] g <- B.runBatch $ do n1 <- B.createNode someProperties _ <- B.addLabels [label1, label2, label3] n1 B.removeLabel label1 n1 neo4jBool $ labelset `elem` map (`G.getNodeLabels` g) (G.getNodes g) _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes g) return () -- | Test batch, get all nodes with a label case_batchAllNodesWithLabel :: Assertion case_batchAllNodesWithLabel = withConnection host port $ do gp <- B.runBatch $ do n1 <- B.createNode someProperties n2 <- B.createNode someProperties n3 <- B.createNode someProperties _ <- B.addLabels [lbl1, lbl2] n1 _ <- B.addLabels [lbl2] n2 B.addLabels [lbl1] n3 g <- B.runBatch $ B.getNodesByLabelAndProperty lbl1 Nothing neo4jEqual 2 (length $ G.getNodes g) neo4jBool $ all (`elem` G.getNodes gp) (G.getNodes g) gempty <- B.runBatch $ B.getNodesByLabelAndProperty "nolbl" Nothing neo4jEqual [] (G.getNodes gempty) _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) return () where lbl1 = "la21bl1" lbl2 = "la21bl2" -- | Test batch, get all nodes with a label and a property case_batchAllNodesWithLabelAndProperty :: Assertion case_batchAllNodesWithLabelAndProperty = withConnection host port $ do gp <- B.runBatch $ do n1 <- B.createNode someProperties n2 <- B.createNode someProperties n3 <- B.createNode anotherProperties _ <- B.addLabels [lbl1, lbl2] n1 _ <- B.addLabels [lbl2] n2 B.addLabels [lbl1] n3 g <- B.runBatch $ B.getNodesByLabelAndProperty lbl1 $ Just ("doublearray", newval [0.1, -12.23 :: Double]) neo4jEqual 1 (length $ G.getNodes g) neo4jBool $ all (`elem` G.getNodes gp) (G.getNodes g) g2 <- B.runBatch $ B.getNodesByLabelAndProperty lbl1 $ Just ("doublearray", newval [0.1, -12.22 :: Double]) neo4jEqual [] (G.getNodes g2) _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) return () where lbl1 = "lbl1" lbl2 = "lbl2" -- | Test cypher basic test case_cypherBasic :: Assertion case_cypherBasic = withConnection host port $ do -- create initial data gp <- B.runBatch $ do n1 <- B.createNode $ M.fromList ["name" |: ("I" :: T.Text)] n2 <- B.createNode $ M.fromList ["name" |: ("you" :: T.Text)] B.createRelationship "know" M.empty n1 n2 -- actual test res <- C.cypher query params neo4jBool $ C.isSuccess res neo4jEqual ["TYPE(r)"] (C.cols $ C.fromSuccess res) neo4jBool $ ["know"] `elem` (C.vals $ C.fromSuccess res) -- cleanup _ <- B.runBatch $ mapM_ B.deleteRelationship (G.getRelationships gp) _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) return () where query = "MATCH (x {name: {startName}})-[r]-(friend) WHERE friend.name = {name} RETURN TYPE(r)" params = M.fromList [("startName", C.newparam ("I" :: T.Text)), ("name", C.newparam ("you" :: T.Text))] -- | Test cypher create a node case_cypherCreateNode :: Assertion case_cypherCreateNode = withConnection host port $ do res <- C.cypher query params neo4jBool $ C.isSuccess res let gp = G.addCypher (C.fromSuccess res) G.empty let props = M.fromList ["name" |: ("Andres" :: T.Text)] neo4jEqual 1 (length $ G.getNodes gp) neo4jBool $ props `elem` map getNodeProperties (G.getNodes gp) -- cleanup _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) return () where query = "CREATE (n:Person { name : {name} }) RETURN n" params = M.fromList [("name", C.newparam ("Andres" :: T.Text))] -- | Test cypher create a node with multiple properties case_cypherCreateNodeMultipleProperties :: Assertion case_cypherCreateNodeMultipleProperties = withConnection host port $ do res <- C.cypher query params neo4jBool $ C.isSuccess res let gp = G.addCypher (C.fromSuccess res) G.empty neo4jEqual 1 (length $ G.getNodes gp) neo4jBool $ props `elem` map getNodeProperties (G.getNodes gp) -- cleanup _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) return () where query = "CREATE (n:Person { props }) RETURN n" props = M.fromList ["name" |: ("Michael" :: T.Text), "position" |: ("Developer" :: T.Text), "awesome" |: True, "children" |: (3 :: Int64)] params = M.fromList [("props", C.ParamProperties props)] -- | Test cypher create multiple nodes case_cypherCreateMultipleNodes :: Assertion case_cypherCreateMultipleNodes = withConnection host port $ do res <- C.cypher query params neo4jBool $ C.isSuccess res let gp = G.addCypher (C.fromSuccess res) G.empty neo4jEqual 2 (length $ G.getNodes gp) neo4jBool $ propsA `elem` map getNodeProperties (G.getNodes gp) neo4jBool $ propsB `elem` map getNodeProperties (G.getNodes gp) -- cleanup _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) return () where query = "CREATE (n:Person { props }) RETURN n" propsA = M.fromList ["name" |: ("Andres" :: T.Text), "position" |: ("Developer" :: T.Text)] propsB = M.fromList ["name" |: ("Michael" :: T.Text), "position" |: ("Developer" :: T.Text)] params = M.fromList [("props", C.ParamPropertiesArray [propsA, propsB])] -- | Test cypher set all properties on a node case_cypherSetAllNodeProperties :: Assertion case_cypherSetAllNodeProperties = withConnection host port $ do res <- C.cypher query params neo4jBool $ C.isSuccess res let gp = G.addCypher (C.fromSuccess res) G.empty neo4jEqual 1 (length $ G.getNodes gp) neo4jBool $ props `elem` map getNodeProperties (G.getNodes gp) -- cleanup _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) return () where query = "CREATE (n:Person { name: 'this property is to be deleted' } ) SET n = { props } RETURN n" props = M.fromList ["name" |: ("Michael" :: T.Text), "position" |: ("Developer" :: T.Text), "awesome" |: True, "children" |: (3 :: Int64)] params = M.fromList [("props", C.ParamProperties props)] -- | Test cypher basic query case_cypherBasicQuery :: Assertion case_cypherBasicQuery = withConnection host port $ do -- create initial data gp <- B.runBatch $ do n1 <- B.createNode $ M.fromList ["name" |: ("I" :: T.Text)] n2 <- B.createNode $ M.fromList ["name" |: ("you" :: T.Text)] n3 <- B.createNode $ M.fromList ["name" |: ("him" :: T.Text), "age" |: (25 :: Int64)] _ <- B.createRelationship "know" M.empty n1 n2 B.createRelationship "know" M.empty n1 n3 -- actual test res <- C.cypher query params neo4jBool $ C.isSuccess res neo4jEqual ["type(r)", "n.name", "n.age"] (C.cols $ C.fromSuccess res) neo4jBool $ ["know", "him", J.Number 25] `elem` (C.vals $ C.fromSuccess res) neo4jBool $ ["know", "you", J.Null] `elem` (C.vals $ C.fromSuccess res) -- cleanup _ <- B.runBatch $ mapM_ B.deleteRelationship (G.getRelationships gp) _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) return () where query = "MATCH (x {name: 'I'})-[r]->(n) RETURN type(r), n.name, n.age" params = M.empty -- | Test cypher basic query get relationships case_cypherBasicQueryGetRels :: Assertion case_cypherBasicQueryGetRels = withConnection host port $ do -- create initial data gp <- B.runBatch $ do n1 <- B.createNode $ M.fromList ["name" |: ("I" :: T.Text)] n2 <- B.createNode $ M.fromList ["name" |: ("you" :: T.Text)] n3 <- B.createNode $ M.fromList ["name" |: ("him" :: T.Text), "age" |: (25 :: Int64)] _ <- B.createRelationship "know" M.empty n1 n2 B.createRelationship "know" M.empty n1 n3 -- actual test res <- C.cypher query params neo4jBool $ C.isSuccess res let gres = G.addCypher (C.fromSuccess res) G.empty neo4jBool $ (length $ G.getRelationships gres) >= 2 neo4jBool $ all (\r -> getRelType r == "know") (G.getRelationships gres) neo4jEqual ["r"] (C.cols $ C.fromSuccess res) -- cleanup _ <- B.runBatch $ mapM_ B.deleteRelationship (G.getRelationships gp) _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) return () where query = "MATCH (x {name: 'I'})-[r]->(n) RETURN r" params = M.empty -- | Test cypher nested results case_cypherNestedResults :: Assertion case_cypherNestedResults = withConnection host port $ do -- create initial data gp <- B.runBatch $ do n1 <- B.createNode $ M.fromList ["name" |: ("I" :: T.Text)] n2 <- B.createNode $ M.fromList ["name" |: ("you" :: T.Text)] B.createRelationship "know" M.empty n1 n2 -- actual test res <- C.cypher query params neo4jBool $ C.isSuccess res neo4jEqual ["collect(n.name)"] (C.cols $ C.fromSuccess res) let v = case (head $ head $ C.vals $ C.fromSuccess res) of J.Array vec -> vec _ -> V.empty neo4jBool $ "you" `V.elem` v neo4jBool $ "I" `V.elem` v -- cleanup _ <- B.runBatch $ mapM_ B.deleteRelationship (G.getRelationships gp) _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) return () where query = "MATCH (n) WHERE n.name in ['I', 'you'] RETURN collect(n.name)" params = M.empty -- | Test cypher errors case_cypherError :: Assertion case_cypherError = withConnection host port $ do -- create initial data gp <- B.runBatch $ do B.createNode $ M.fromList ["age" |: (26 :: Int64)] -- actual test res <- C.cypher query params neo4jBool $ res `elem` [Left "BadInputException", Left "ArithmeticException"] -- cleanup _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) return () where query = "MATCH (x) WHERE has(x.age) RETURN x.age / 0" params = M.empty -- | Test lone transaction cypher basic query case_loneTransactionBasicQuery :: Assertion case_loneTransactionBasicQuery = withConnection host port $ do -- create initial data gp <- B.runBatch $ do n1 <- B.createNode $ M.fromList ["name" |: ("I" :: T.Text)] n2 <- B.createNode $ M.fromList ["name" |: ("you" :: T.Text)] n3 <- B.createNode $ M.fromList ["name" |: ("him" :: T.Text), "age" |: (25 :: Int64)] _ <- B.createRelationship "know" M.empty n1 n2 B.createRelationship "know" M.empty n1 n3 -- actual test res <- TC.loneQuery query params neo4jBool $ TC.isSuccess res neo4jEqual ["type(r)", "n.name", "n.age"] (TC.cols $ TC.fromSuccess res) neo4jBool $ ["know", "him", J.Number 25] `elem` (TC.vals $ TC.fromSuccess res) neo4jBool $ ["know", "you", J.Null] `elem` (TC.vals $ TC.fromSuccess res) neo4jEqual TC.emptyStats (TC.stats $ TC.fromSuccess res) -- cleanup _ <- B.runBatch $ mapM_ B.deleteRelationship (G.getRelationships gp) _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) return () where query = "MATCH (x {name: 'I'})-[r]->(n) RETURN type(r), n.name, n.age" params = M.empty -- | Test cypher errors in a transaction case_cypherTransactionError :: Assertion case_cypherTransactionError = withConnection host port $ do -- create initial data gp <- B.runBatch $ B.createNode $ M.fromList ["age" |: (26 :: Int64)] -- actual test res <- TC.loneQuery query params neo4jEqual (Left ("Neo.ClientError.Statement.ArithmeticError","/ by zero")) res -- cleanup _ <- B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) return () where query = "MATCH (x) WHERE has(x.age) RETURN x.age / 0" params = M.empty -- | Test a cypher transaction case_cypherTransaction :: Assertion case_cypherTransaction = withConnection host port $ do res <- TC.runTransaction $ do result <- TC.cypher "CREATE (pere: PERSON {age: {age}}) CREATE (pau: PERSON {props}) \ \CREATE p1 = (pere)-[:KNOWS]->(pau) RETURN pere, pau, p1, pere.age" $ M.fromList [("age", TC.newparam (78 :: Int64)), ("props", TC.ParamProperties $ M.fromList["age" |: (99 :: Int64)])] voidIO $ assertEqual "" ["pere", "pau", "p1", "pere.age"] (TC.cols result) voidIO $ assertEqual "" (TC.Stats True 2 0 2 1 0 2 0 0 0 0 0) (TC.stats result) let vals = TC.vals result voidIO $ assertEqual "" 1 (length vals) voidIO $ assertEqual "" 4 (length $ head vals) voidIO $ assertEqual "" (J.Number 78.0) (head vals !! 3) voidIO $ assertEqual "" 1 (length $ TC.graph result) voidIO $ assertEqual "" 1 (length $ G.getRelationships (head $ TC.graph result)) voidIO $ assertEqual "" 2 (length $ G.getNodes (head $ TC.graph result)) return result neo4jBool $ TC.isSuccess res let (Right tresult) = res let gp = head $ TC.graph tresult void $ B.runBatch $ mapM_ B.deleteRelationship (G.getRelationships gp) void $ B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) where voidIO x = void $ liftIO x -- | Test a cypher transaction with a explicit commit case_cypherTransactionExplicitCommit :: Assertion case_cypherTransactionExplicitCommit = withConnection host port $ do res <- TC.runTransaction $ do result <- TC.cypher "CREATE (pere: PERSON {age: {age}}) CREATE (pau: PERSON {props}) \ \CREATE p1 = (pere)-[:KNOWS]->(pau) RETURN pere, pau, p1, pere.age" $ M.fromList [("age", TC.newparam (78 :: Int64)), ("props", TC.ParamProperties $ M.fromList["age" |: (99 :: Int64)])] void $ TC.cypher "CREATE (pep: PERSON {age: 55})" M.empty TC.commit return result neo4jBool $ TC.isSuccess res let (Right result) = res voidIO $ assertEqual "" ["pere", "pau", "p1", "pere.age"] (TC.cols result) voidIO $ assertEqual "" (TC.Stats True 2 0 2 1 0 2 0 0 0 0 0) (TC.stats result) let vals = TC.vals result voidIO $ assertEqual "" 1 (length vals) voidIO $ assertEqual "" 4 (length $ head vals) voidIO $ assertEqual "" (J.Number 78.0) (head vals !! 3) voidIO $ assertEqual "" 1 (length $ TC.graph result) voidIO $ assertEqual "" 1 (length $ G.getRelationships (head $ TC.graph result)) voidIO $ assertEqual "" 2 (length $ G.getNodes (head $ TC.graph result)) let gp = head $ TC.graph result void $ B.runBatch $ mapM_ B.deleteRelationship (G.getRelationships gp) void $ B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) where voidIO x = void $ liftIO x -- | Test a cypher transaction with a keepalive case_cypherTransactionKeepAlive :: Assertion case_cypherTransactionKeepAlive = withConnection host port $ do res <- TC.runTransaction $ do TC.keepalive void $ TC.cypher "CREATE (pep: PERSON {age: 55})" M.empty TC.keepalive result <- TC.cypher "CREATE (pere: PERSON {age: {age}}) CREATE (pau: PERSON {props}) \ \CREATE p1 = (pere)-[:KNOWS]->(pau) RETURN pere, pau, p1, pere.age" $ M.fromList [("age", TC.newparam (78 :: Int64)), ("props", TC.ParamProperties $ M.fromList["age" |: (99 :: Int64)])] return result neo4jBool $ TC.isSuccess res let (Right result) = res voidIO $ assertEqual "" ["pere", "pau", "p1", "pere.age"] (TC.cols result) voidIO $ assertEqual "" (TC.Stats True 2 0 2 1 0 2 0 0 0 0 0) (TC.stats result) let vals = TC.vals result voidIO $ assertEqual "" 1 (length vals) voidIO $ assertEqual "" 4 (length $ head vals) voidIO $ assertEqual "" (J.Number 78.0) (head vals !! 3) voidIO $ assertEqual "" 1 (length $ TC.graph result) voidIO $ assertEqual "" 1 (length $ G.getRelationships (head $ TC.graph result)) voidIO $ assertEqual "" 2 (length $ G.getNodes (head $ TC.graph result)) let gp = head $ TC.graph result void $ B.runBatch $ mapM_ B.deleteRelationship (G.getRelationships gp) void $ B.runBatch $ mapM_ B.deleteNode (G.getNodes gp) where voidIO x = void $ liftIO x -- | Test a cypher transaction with an error case_cypherErrorInTransaction :: Assertion case_cypherErrorInTransaction = withConnection host port $ do res <- TC.runTransaction $ do -- query with wrong syntax void $ TC.cypher "i" M.empty TC.cypher "CREATE (pere: PERSON {age: {age}}) CREATE (pau: PERSON {props}) \ \CREATE p1 = (pere)-[:KNOWS]->(pau) RETURN pere, pau, p1, pere.age" $ M.fromList [("age", TC.newparam (78 :: Int64)), ("props", TC.ParamProperties $ M.fromList["age" |: (99 :: Int64)])] neo4jBool $ not $ TC.isSuccess res neo4jEqual (Left ("Neo.ClientError.Statement.InvalidSyntax", "")) (procRes res) where procRes r@(Right _) = r procRes r@(Left (err, _))= Left (err, "") -- | Test a cypher rollback transaction case_cypherTransactionRollback :: Assertion case_cypherTransactionRollback = withConnection host port $ do void $ TC.runTransaction $ do void $ TC.cypher "CREATE (pepe: PERSON {age: 55})" M.empty result <- TC.cypher "CREATE (pere: PERSON {age: {age}}) CREATE (pau: PERSON {props}) \ \CREATE p1 = (pere)-[:KNOWS]->(pau) RETURN pere, pau, p1, pere.age" $ M.fromList [("age", TC.newparam (78 :: Int64)), ("props", TC.ParamProperties $ M.fromList["age" |: (99 :: Int64)])] TC.rollback return result -- | Test a cypher rollback and leave transaction case_cypherTransactionRollbackAndLeave :: Assertion case_cypherTransactionRollbackAndLeave = withConnection host port $ do res <- TC.runTransaction $ do void $ TC.cypher "CREATE (pepe: PERSON {age: 55})" M.empty result <- TC.cypher "CREATE (pere: PERSON {age: {age}}) CREATE (pau: PERSON {props}) \ \CREATE p1 = (pere)-[:KNOWS]->(pau) RETURN pere, pau, p1, pere.age" $ M.fromList [("age", TC.newparam (78 :: Int64)), ("props", TC.ParamProperties $ M.fromList["age" |: (99 :: Int64)])] TC.rollbackAndLeave "My message" TC.commit TC.rollback return result neo4jEqual (Left ("Rollback", "My message")) res -- | Test issuing a commits after a rollback case_cypherTransactionEnded :: Assertion case_cypherTransactionEnded = assertException expException $ withConnection host port $ do void $ TC.runTransaction $ do void $ TC.cypher "CREATE (pepe: PERSON {age: 55})" M.empty result <- TC.cypher "CREATE (pere: PERSON {age: {age}}) CREATE (pau: PERSON {props}) \ \CREATE p1 = (pere)-[:KNOWS]->(pau) RETURN pere, pau, p1, pere.age" $ M.fromList [("age", TC.newparam (78 :: Int64)), ("props", TC.ParamProperties $ M.fromList["age" |: (99 :: Int64)])] TC.rollback TC.commit return result where expException = TransactionEndedExc -- | Test graph union op case_graphUnion :: Assertion case_graphUnion = withConnection host port $ do g <- B.runBatch $ do n1 <- B.createNode someProperties n2 <- B.createNode anotherProperties void $ B.addLabels ["a"] n1 _ <- B.createRelationship "type1" someOtherProperties n1 n2 B.createRelationship "type2" someProperties n2 n1 g2 <- B.runBatch $ do n1 <- B.createNode someOtherProperties n2 <- B.createNode anotherProperties void $ B.addLabels ["b"] n1 _ <- B.createRelationship "type1" someOtherProperties n1 n2 B.createRelationship "type2" someProperties n2 n1 let g3 = g `G.union` g2 neo4jEqual 4 (length $ G.getNodes g3) neo4jEqual 4 (length $ G.getRelationships g3) neo4jBool $ someOtherProperties `elem` map getRelProperties (G.getRelationships g3) neo4jBool $ someProperties `elem` map getRelProperties (G.getRelationships g3) neo4jBool $ someOtherProperties `elem` map getNodeProperties (G.getNodes g3) neo4jBool $ someProperties `elem` map getNodeProperties (G.getNodes g3) neo4jBool $ "type1" `elem` map getRelType (G.getRelationships g3) neo4jBool $ "type2" `elem` map getRelType (G.getRelationships g3) neo4jBool $ "a" `elem` (join $ map (HS.toList . (flip G.getNodeLabels g3)) (G.getNodes g3)) neo4jBool $ "b" `elem` (join $ map (HS.toList . (flip G.getNodeLabels g3)) (G.getNodes g3)) mapM_ deleteRelationship (G.getRelationships g3) mapM_ deleteNode (G.getNodes g3) -- | Test graph difference op case_graphDifference :: Assertion case_graphDifference = withConnection host port $ do g <- B.runBatch $ do n1 <- B.createNode someProperties n2 <- B.createNode anotherProperties void $ B.addLabels ["a"] n1 _ <- B.createRelationship "type1" someOtherProperties n1 n2 B.createRelationship "type2" someProperties n2 n1 let g2 = g `G.difference` g neo4jEqual 0 (length $ G.getRelationships g2) neo4jEqual 0 (length $ G.getNodes g2) let subGraph = G.deleteNode (head $ G.getNodes g) g let g3 = g `G.difference` (G.deleteRelationship (head $ G.getRelationships subGraph) subGraph) neo4jEqual 1 (length $ G.getRelationships g3) neo4jEqual 1 (length $ G.getNodes g3) -- | Test graph intersection op case_graphIntersection :: Assertion case_graphIntersection = withConnection host port $ do g <- B.runBatch $ do n1 <- B.createNode someProperties n2 <- B.createNode anotherProperties void $ B.addLabels ["a"] n1 _ <- B.createRelationship "type1" someOtherProperties n1 n2 B.createRelationship "type2" someProperties n2 n1 g2 <- B.runBatch $ do B.createNode someProperties let g3 = G.addNode (head $ G.getNodes g) g2 let g4 = g `G.intersection` (G.addRelationship (head $ G.getRelationships g) g3) neo4jEqual 1 (length $ G.getRelationships g4) neo4jEqual 1 (length $ G.getNodes g4) neo4jEqual (head $ G.getRelationships g) (head $ G.getRelationships g4) neo4jEqual (head $ G.getNodes g) (head $ G.getNodes g4)