hzk-2.0.0: src/test-zookeeper.hs
{-# LANGUAGE OverloadedStrings #-}
-- This file is part of zhk
--
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without modification,
-- are permitted provided that the following conditions are met:
--
-- Redistributions of source code must retain the above copyright notice, this
-- list of conditions and the following disclaimer.
--
-- Redistributions in binary form must reproduce the above copyright notice, this
-- list of conditions and the following disclaimer in the documentation and/or
-- other materials provided with the distribution.
--
-- Neither the name of the {organization} nor the names of its
-- contributors may be used to endorse or promote products derived from
-- this software without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-- ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
-- ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-- ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
module Main where
import Data.Maybe
import Test.Tasty
import System.Exit
import Control.Monad
import Test.Tasty.HUnit
import Control.Concurrent
import Database.Zookeeper
import System.Environment
envKey :: String
envKey = "_ZOOKEEPER_ENDPOINT"
chroot :: String -> String
chroot = ("/test-zookeeper" ++)
getEndpoint :: IO String
getEndpoint = do
mendpoint <- fmap (lookup envKey) getEnvironment
case mendpoint of
Nothing -> return "localhost:2181"
Just v -> return v
disclaimer :: IO ()
disclaimer = do
endpoint <- getEndpoint
putStrLn ("> This program depends on a zookeeper server. The current endpoint is: " ++ endpoint)
putStrLn ("> You may override this default with the following env variable: " ++ envKey)
testExists zh = testGroup "exists"
[ testCase "exists after create" $ do
let path = chroot "/testExists#1"
create zh path Nothing OpenAclUnsafe []
exists zh path Nothing >>= (@? "== Right _") . (either (const False) (const True))
, testCase "exists without znode" $ do
let path = chroot "/testExists#2"
exists zh path Nothing >>= (@?= Left NoNodeError)
, testCase "exists(watcher) and create" $ do
let path = chroot "/testExists#3"
mvar <- newEmptyMVar
exists zh path (Just $ watcher mvar) >>= (@?= Left NoNodeError)
create zh path Nothing OpenAclUnsafe []
takeMVar mvar >>= (@?= (CreatedEvent, Just path))
, testCase "exists(watcher) and set" $ do
let path = chroot "/testExists#4"
mvar <- newEmptyMVar
create zh path Nothing OpenAclUnsafe []
exists zh path (Just $ watcher mvar)
set zh path Nothing Nothing
takeMVar mvar >>= (@?= (ChangedEvent, Just path))
, testCase "exists(watcher) and delete" $ do
let path = chroot "/testExists#5"
mvar <- newEmptyMVar
create zh path Nothing OpenAclUnsafe []
exists zh path (Just $ watcher mvar)
delete zh path Nothing
takeMVar mvar >>= (@?= (DeletedEvent, Just path))
]
where
watcher mvar _ event _ mpath = putMVar mvar (event, mpath)
testGet zh = testGroup "get"
[ testCase "get without znode" $ do
let path = chroot "/testGet#1"
get zh path Nothing >>= (@?= Left NoNodeError)
, testCase "create(nodata) and get" $ do
let path = chroot "/testGet#2"
create zh path Nothing OpenAclUnsafe []
get zh path Nothing >>= (@? "== Right (Nothing, _)") . (either (const False) (isNothing . fst))
, testCase "create(data) and get" $ do
let path = chroot "/testGet#3"
create zh path (Just "foobar") OpenAclUnsafe []
get zh path Nothing >>= (@? "== Right (Just \"foobar\", _)") . (either (const False) ((== Just "foobar") . fst))
, testCase "get(watcher) and set" $ do
let path = chroot "/testGet#4"
mvar <- newEmptyMVar
create zh path Nothing OpenAclUnsafe []
get zh path (Just $ watcher mvar)
set zh path Nothing Nothing
takeMVar mvar >>= (@?= (ChangedEvent, Just path))
, testCase "get(watcher) and delete" $ do
let path = chroot "/testGet#5"
mvar <- newEmptyMVar
create zh path Nothing OpenAclUnsafe []
get zh path (Just $ watcher mvar)
delete zh path Nothing
takeMVar mvar >>= (@?= (DeletedEvent, Just path))
]
where
watcher mvar _ event _ mpath = putMVar mvar (event, mpath)
testOwnsEphemeral zh = testGroup "ownsEphemeral"
[ testCase "get ephemeral" $ do
let path = chroot "/testGet#6"
create zh path Nothing OpenAclUnsafe [Ephemeral]
(Right (_, stat)) <- get zh path Nothing
myId <- getClientId zh
ownsEphemeral myId stat @? "owns ephemeral"
]
testGetChildren zh = testGroup "getChildren"
[ testCase "getChildren without znode" $ do
let path = chroot "/testGetChildren#1"
getChildren zh path Nothing >>= (@?= Left NoNodeError)
, testCase "getChildren after create" $ do
let path = chroot "/testGetChildren#2"
create zh path Nothing OpenAclUnsafe []
getChildren zh path Nothing >>= (@?= Right [])
, testCase "getChildren with one child" $ do
let path = chroot "/testGetChildren#3"
create zh path Nothing OpenAclUnsafe []
create zh (path ++ "/1") Nothing OpenAclUnsafe []
getChildren zh path Nothing >>= (@?= Right ["1"])
, testCase "getChildren(watcher) and create child" $ do
let path = chroot "/testGetChildren#4"
mvar <- newEmptyMVar
create zh path Nothing OpenAclUnsafe []
getChildren zh path (Just $ watcher mvar)
create zh (path ++ "/1") Nothing OpenAclUnsafe []
takeMVar mvar >>= (@?= (ChildEvent, Just path))
, testCase "getChildren(watcher) and delete child" $ do
let path = chroot "/testGetChildren#5"
mvar <- newEmptyMVar
create zh path Nothing OpenAclUnsafe []
create zh (path ++ "/1") Nothing OpenAclUnsafe []
getChildren zh path (Just $ watcher mvar)
delete zh (path ++ "/1") Nothing
takeMVar mvar >>= (@?= (ChildEvent, Just path))
]
where
watcher mvar _ event _ mpath = putMVar mvar (event, mpath)
testGetAcl zh = testGroup "getAcl"
[ testCase "getAcl" $ do
let path = chroot "/testGetAcl#1"
create zh path Nothing OpenAclUnsafe []
getAcl zh path >>= (@?= 1) . (either (const 0) (countAcls . fst))
, testCase "getAcl flags" $ do
let path = chroot "/testGetAcl#2"
flags = [ []
, [CanRead]
, [CanRead, CanAdmin]
, [CanRead, CanAdmin, CanWrite]
, [CanRead, CanAdmin, CanWrite, CanCreate]
, [CanRead, CanAdmin, CanWrite, CanCreate, CanDelete]
]
forM_ flags $ \flag -> do
create zh path Nothing (List [Acl "world" "anyone" flag]) []
rc <- getAcl zh path
delete zh path Nothing
(either (const []) (getFlags . fst) rc) @?= flag
]
where
countAcls (List xs) = length xs
countAcls _ = 1
getFlags (List xs) = concatMap aclFlags xs
getFlags _ = []
rmrf :: Zookeeper -> String -> IO ()
rmrf zh path = do
let childPath name = path ++ "/" ++ name
children <- getChildren zh path Nothing
case children of
Right xs -> mapM_ (rmrf zh . childPath) xs
Left _ -> return ()
void $ delete zh path Nothing
main :: IO ()
main = do
disclaimer
endpoint <- getEndpoint
waitState <- newEmptyMVar
withZookeeper endpoint 5000 (Just $ watcher waitState) Nothing $ \zh -> do
state <- takeMVar waitState
case state of
ConnectedState -> do
rmrf zh (chroot "")
create zh (chroot "") Nothing OpenAclUnsafe []
defaultMain $ testGroup "Zookeeper" [ testGet zh
, testExists zh
, testGetAcl zh
, testGetChildren zh
, testOwnsEphemeral zh
]
_ -> exitFailure
where
watcher mvar _ _ e _ = putMVar mvar e