{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} import Control.Concurrent import Control.Monad.IO.Class import Data.Maybe import Data.Text (Text) import Data.UUID import Network.Consul (deleteKey,getKey,getSessionInfo,initializeConsulClient,putKey,withSession,ConsulClient(..),runService,getServiceHealth) import Network.Consul.Types import qualified Network.Consul.Internal as I import Network.HTTP.Client import Network.Socket (PortNumber(..)) import System.Random import Test.Tasty import Test.Tasty.HUnit client :: IO ConsulClient client = initializeConsulClient "localhost" 8500 Nothing {- Internal Tests -} internalKVTests :: TestTree internalKVTests = testGroup "Internal Key Value" [testGetInvalidKey, testPutKey, testGetKey,testGetKeys,testListKeys,testDeleteKey,testGetNullValueKey,testDeleteRecursive] testGetInvalidKey :: TestTree testGetInvalidKey = testCase "testGetInvalidKey" $ do _client@ConsulClient{..} <- client x <- I.getKey ccManager (I.hostWithScheme _client) ccPort "nokey" Nothing Nothing Nothing assertEqual "testGetInvalidKey: Found a key that doesn't exist" x Nothing testPutKey :: TestTree testPutKey = testCase "testPutKey" $ do _client@ConsulClient{..} <- client let put = KeyValuePut "/testPutKey" "Test" Nothing Nothing x <- I.putKey ccManager (I.hostWithScheme _client) ccPort put Nothing assertEqual "testPutKey: Write failed" True x testGetKey :: TestTree testGetKey = testCase "testGetKey" $ do _client@ConsulClient{..} <- client let put = KeyValuePut "/testGetKey" "Test" Nothing Nothing x1 <- I.putKey ccManager (I.hostWithScheme _client) ccPort put Nothing assertEqual "testGetKey: Write failed" True x1 x2 <- I.getKey ccManager (I.hostWithScheme _client) ccPort "/testGetKey" Nothing Nothing Nothing case x2 of Just x -> assertEqual "testGetKey: Incorrect Value" (kvValue x) (Just "Test") Nothing -> assertFailure "testGetKey: No value returned" testGetNullValueKey :: TestTree testGetNullValueKey = testCase "testGetNullValueKey" $ do _client@ConsulClient{..} <- client let put = KeyValuePut "/testGetNullValueKey" "" Nothing Nothing x1 <- I.putKey ccManager (I.hostWithScheme _client) ccPort put Nothing assertEqual "testGetNullValueKey: Write failed" True x1 liftIO $ threadDelay (500 * 1000) x2 <- I.getKey ccManager (I.hostWithScheme _client) ccPort "/testGetNullValueKey" Nothing Nothing Nothing case x2 of Just x -> assertEqual "testGetNullValueKey: Incorrect Value" (kvValue x) Nothing Nothing -> assertFailure "testGetNullValueKey: No value returned" testGetKeys :: TestTree testGetKeys = testCase "testGetKeys" $ do _client@ConsulClient{..} <- client let put1 = KeyValuePut "/testGetKeys/key1" "Test" Nothing Nothing x1 <- I.putKey ccManager (I.hostWithScheme _client) ccPort put1 Nothing assertEqual "testGetKeys: Write failed" True x1 let put2 = KeyValuePut "/testGetKeys/key2" "Test" Nothing Nothing x2 <- I.putKey ccManager (I.hostWithScheme _client) ccPort put2 Nothing assertEqual "testGetKeys: Write failed" True x2 x3 <- I.getKeys ccManager (I.hostWithScheme _client) ccPort "/testGetKeys" Nothing Nothing Nothing assertEqual "testGetKeys: Incorrect number of results" 2 (length x3) testListKeys :: TestTree testListKeys = testCase "testListKeys" $ do _client@ConsulClient{..} <- client let put1 = KeyValuePut "/testListKeys/key1" "Test" Nothing Nothing x1 <- I.putKey ccManager (I.hostWithScheme _client) ccPort put1 Nothing assertEqual "testListKeys: Write failed" True x1 let put2 = KeyValuePut "/testListKeys/key2" "Test" Nothing Nothing x2 <- I.putKey ccManager (I.hostWithScheme _client) ccPort put2 Nothing assertEqual "testListKeys: Write failed" True x2 x3 <- I.listKeys ccManager (I.hostWithScheme _client) ccPort "/testListKeys/" Nothing Nothing Nothing assertEqual "testListKeys: Incorrect number of results" 2 (length x3) testDeleteKey :: TestTree testDeleteKey = testCase "testDeleteKey" $ do _client@ConsulClient{..} <- client let put1 = KeyValuePut "/testDeleteKey" "Test" Nothing Nothing x1 <- I.putKey ccManager (I.hostWithScheme _client) ccPort put1 Nothing assertEqual "testDeleteKey: Write failed" True x1 x2 <- I.deleteKey ccManager (I.hostWithScheme _client) ccPort "/testDeleteKey" False Nothing assertEqual "testDeleteKey: Delete Failed" True x2 x3 <- I.getKey ccManager (I.hostWithScheme _client) ccPort "/testDeleteKey" Nothing Nothing Nothing assertEqual "testDeleteKey: Key was not deleted" Nothing x3 testDeleteRecursive :: TestTree testDeleteRecursive = testCase "testDeleteRecursive" $ do _client@ConsulClient{..} <- client let put1 = KeyValuePut "/testDeleteRecursive/1" "Test" Nothing Nothing put2 = KeyValuePut "/testDeleteRecursive/2" "Test" Nothing Nothing x1 <- I.putKey ccManager (I.hostWithScheme _client) ccPort put1 Nothing assertEqual "testDeleteKey: Write failed" True x1 x2 <- I.putKey ccManager (I.hostWithScheme _client) ccPort put2 Nothing assertEqual "testDeleteKey: Write failed" True x2 I.deleteKey ccManager (I.hostWithScheme _client) ccPort "/testDeleteRecursive/" True Nothing x3 <- I.getKey ccManager (I.hostWithScheme _client) ccPort "/testDeleteRecursive/1" Nothing Nothing Nothing assertEqual "testDeleteKey: Key was not deleted" Nothing x3 {- Client KV -} clientKVTests :: TestTree clientKVTests = testGroup "Client KV Tests" [testDeleteRecursiveClient] testDeleteRecursiveClient :: TestTree testDeleteRecursiveClient = testCase "testDeleteRecursiveClient" $ do c <- client let put1 = KeyValuePut "/testDeleteRecursive/1" "Test" Nothing Nothing put2 = KeyValuePut "/testDeleteRecursive/2" "Test" Nothing Nothing x1 <- putKey c put1 Nothing assertEqual "testDeleteKey: Write failed" True x1 x2 <- putKey c put2 Nothing assertEqual "testDeleteKey: Write failed" True x2 deleteKey c "/testDeleteRecursive/" True Nothing x3 <- getKey c "/testDeleteRecursive/1" Nothing Nothing Nothing assertEqual "testDeleteKey: Key was not deleted" Nothing x3 {- Agent -} testRegisterService :: TestTree testRegisterService = testCase "testRegisterService" $ do _client@ConsulClient{..} <- client let req = RegisterService Nothing "testService" ["test"] Nothing (Just $ Ttl "10s") val <- I.registerService ccManager (I.hostWithScheme _client) ccPort req Nothing assertEqual "testRegisterService: Service was not created" val True mService <- I.getService ccManager (I.hostWithScheme _client) ccPort "testService" Nothing Nothing case mService of Just _ -> return () Nothing -> assertFailure "testRegisterService: Service was not found" testGetSelf :: TestTree testGetSelf = testCase "testGetSelf" $ do _client@ConsulClient{..} <- client x <- I.getSelf ccManager (I.hostWithScheme _client) ccPort assertEqual "testGetSelf: Self not returned" True (isJust x) {- testRegisterHealthCheck :: TestTree testRegisterHealthCheck = testCase "testRegisterHealthCheck" $ do _client@ConsulClient{..} <- client let check = RegisterHealthCheck "testHealthCheck" "testHealthCheck" "" Nothing Nothing (Just "15s") x1 <- I.registerHealthCheck ccManager (I.hostWithScheme _client) ccPort check undefined -} {- Health Checks -} testGetServiceHealth :: TestTree testGetServiceHealth = testCase "testGetServiceHealth" $ do _client@ConsulClient{..} <- client let req = RegisterService (Just "testGetServiceHealth") "testGetServiceHealth" [] Nothing Nothing r1 <- I.registerService ccManager (I.hostWithScheme _client) ccPort req Nothing case r1 of True -> do liftIO $ threadDelay 1000000 r2 <- I.getServiceHealth ccManager (I.hostWithScheme _client) ccPort "testGetServiceHealth" case r2 of Just [x] -> return () Just [] -> assertFailure "testGetServiceHealth: No Services Returned" Nothing -> assertFailure "testGetServiceHealth: Failed to parse result" False -> assertFailure "testGetServiceHealth: Service was not created" testHealth :: TestTree testHealth = testGroup "Health Check Tests" [testGetServiceHealth] {- Session -} testCreateSession :: TestTree testCreateSession = testCase "testCreateSession" $ do _client@ConsulClient{..} <- client let req = SessionRequest Nothing (Just "testCreateSession") Nothing ["serfHealth"] (Just Release) (Just "30s") result <- I.createSession ccManager (I.hostWithScheme _client) ccPort req Nothing case result of Just _ -> return () Nothing -> assertFailure "testCreateSession: No session was created" testGetSessionInfo :: TestTree testGetSessionInfo = testCase "testGetSessionInfo" $ do _client@ConsulClient{..} <- client let req = SessionRequest Nothing (Just "testGetSessionInfo") Nothing ["serfHealth"] (Just Release) (Just "30s") result <- I.createSession ccManager (I.hostWithScheme _client) ccPort req Nothing case result of Just x -> do x1 <- I.getSessionInfo ccManager (I.hostWithScheme _client) ccPort x Nothing case x1 of Just _ -> return () Nothing -> assertFailure "testGetSessionInfo: Session Info was not returned" Nothing -> assertFailure "testGetSessionInfo: No session was created" testRenewSession :: TestTree testRenewSession = testCase "testRenewSession" $ do _client@ConsulClient{..} <- client let req = SessionRequest Nothing (Just "testRenewSession") Nothing ["serfHealth"] (Just Release) (Just "30s") result <- I.createSession ccManager (I.hostWithScheme _client) ccPort req Nothing case result of Just x -> do x1 <- I.renewSession ccManager (I.hostWithScheme _client) ccPort x Nothing case x1 of True -> return () False -> assertFailure "testRenewSession: Session was not renewed" Nothing -> assertFailure "testRenewSession: No session was created" testRenewNonexistentSession :: TestTree testRenewNonexistentSession = testCase "testRenewNonexistentSession" $ do _client@ConsulClient{..} <- client sessId :: UUID <- randomIO let session = Session (toText sessId) Nothing x <- I.renewSession ccManager (I.hostWithScheme _client) ccPort session Nothing case x of True -> assertFailure "testRenewNonexistentSession: Non-existent session was renewed" False -> return () testDestroySession :: TestTree testDestroySession = testCase "testDestroySession" $ do _client@ConsulClient{..} <- client let req = SessionRequest Nothing (Just "testDestroySession") Nothing ["serfHealth"] (Just Release) (Just "30s") result <- I.createSession ccManager (I.hostWithScheme _client) ccPort req Nothing case result of Just x -> do _ <- I.destroySession ccManager (I.hostWithScheme _client) ccPort x Nothing x1 <- I.getSessionInfo ccManager (I.hostWithScheme _client) ccPort x Nothing assertBool "testDestroySession: Session info was returned after destruction" $ (x1 == Nothing) || (x1 == Just []) Nothing -> assertFailure "testDestroySession: No session was created" testInternalSession :: TestTree testInternalSession = testGroup "Internal Session Tests" [testCreateSession, testGetSessionInfo, testRenewSession, testRenewNonexistentSession, testDestroySession] testSessionMaintained :: TestTree testSessionMaintained = testCase "testSessionMaintained" $ do client@ConsulClient{..} <- client let req = SessionRequest Nothing (Just "testSessionMaintained") Nothing ["serfHealth"] (Just Release) (Just "10s") result <- I.createSession ccManager (I.hostWithScheme client) ccPort req Nothing case result of Just session -> do threadDelay (12 * 1000000) y <- getSessionInfo client session Nothing assertEqual "testSessionMaintained: Session not found" True (isJust y) Nothing -> assertFailure "testSessionMaintained: No Session was created" testWithSessionCancel :: TestTree testWithSessionCancel = testCase "testWithSessionCancel" $ do client@ConsulClient{..} <- initializeConsulClient "localhost" 8500 Nothing let req = SessionRequest Nothing (Just "testWithSessionCancel") Nothing ["serfHealth"] (Just Release) (Just "10s") result <- I.createSession ccManager (I.hostWithScheme client) ccPort req Nothing case result of Just session -> do x1 <- withSession client Nothing 5 session (\ y -> action y client ) cancelAction assertEqual "testWithSessionCancel: Incorrect value" "Canceled" x1 z <- getSessionInfo client session Nothing assertBool "testWithSessionCancel: Session was found" $ (z == Nothing) || (z == Just []) Nothing -> assertFailure "testWithSessionCancel: No session was created" where action :: MonadIO m => Session -> ConsulClient -> m Text action x client@ConsulClient{..} = do I.destroySession ccManager (I.hostWithScheme client) ccPort x Nothing liftIO $ threadDelay (30 * 1000000) return ("NotCanceled" :: Text) cancelAction :: MonadIO m => m Text cancelAction = return ("Canceled" :: Text) testRunServiceTtl :: TestTree testRunServiceTtl = testCase "testRunServiceTtl" $ do client@ConsulClient{..} <- initializeConsulClient "localhost" 8500 Nothing let register = RegisterService Nothing "testRunServiceTtl" [] (Just 8000) $ Just $ Ttl "10s" runService client register (action client) Nothing where action client = do threadDelay 15000000 mHealth <- getServiceHealth client "testRunServiceTtl" case mHealth of Nothing -> assertFailure "testRunServiceTtl: No healthcheck was found" Just [x] -> do let checks = hChecks x mapM_ (testCheck) checks testCheck check = do assertBool "testRunServiceTtl: Check not passing" $ cStatus check == Passing sessionWorkflowTests :: TestTree sessionWorkflowTests = testGroup "Session Workflow Tests" [testWithSessionCancel,testSessionMaintained] runServiceTests :: TestTree runServiceTests = testGroup "Run Service Tests" [testRunServiceTtl] agentTests :: TestTree agentTests = testGroup "Agent Tests" [testGetSelf,testRegisterService] allTests :: TestTree allTests = testGroup "All Tests" [testInternalSession, internalKVTests, sessionWorkflowTests, agentTests,testHealth, clientKVTests, runServiceTests] main :: IO () main = defaultMain allTests