packages feed

orchestrate-0.2.0.3: specs/Specs/Orchestrate/EventSpec.hs

{-# LANGUAGE CPP               #-}
{-# LANGUAGE NamedFieldPuns    #-}
{-# LANGUAGE OverloadedStrings #-}


module Specs.Orchestrate.EventSpec where


import           Control.Applicative
import           Control.Error
import           Control.Exception
import           Control.Lens
import           Control.Monad
import qualified Data.Text                     as T
import           Network.Wreq

import           Test.Hspec

import           Database.Orchestrate.Events
import           Database.Orchestrate.KeyValue
import           Database.Orchestrate.Types
import           Database.Orchestrate.Utils

import           Specs.Orchestrate.Spec.Types
import           Specs.Orchestrate.Spec.Utils


type GetEvent = IO (Either SomeException (Maybe (EventItem Event Person)))

fixtures :: [Person]
fixtures = [ Person "joe" 42
           , Person "ella" 13
           ]

events :: [(Event, Timestamp)]
events = [ (Event "birth"    0.0, 784111777000)
         , (Event "marriage" 0.5, 784111777100)
         , (Event "child"    0.6, 784111777200)
         , (Event "divorce"  0.8, 784111777300)
         , (Event "death"    1.0, 784111777400)
         ]

deleteLocs :: [Location] -> IO ()
deleteLocs locs = do
    Session{_sessionOptions,_sessionURL} <- envSession
    let url = T.unpack _sessionURL
    mapM_ (deleteWith _sessionOptions . (++ "?purge=true") . (url ++) . T.unpack) locs

withEvents :: Person -> EventType -> [(Event, Timestamp)] -> IO () -> IO ()
withEvents p et evs = withEvents' p et evs . const

withEvents' :: Person -> EventType -> [(Event, Timestamp)]
            -> (Either SomeException [Location] -> IO ())
            -> IO ()
withEvents' p et evs action =
    bracket (run . mapM (uncurry (createEvent p et)) $ map (fmap Just) evs)
            (either (const $ return ()) deleteLocs)
            action

callLoc :: Location -> (Timestamp -> Int -> OrchestrateIO a)
        -> IO (Either SomeException a)
callLoc loc f = run . join $ f <$> eithererr loc (loc ^? locationTimestamp)
                               <*> eithererr loc (loc ^? locationOrdinal)

eithererr :: Monad m => Location -> Maybe a -> OrchestrateT m a
eithererr loc = orchestrateEither
              . note (SomeException
                        (ErrorCall $ "Invalid location: " ++ T.unpack loc))

#if NETWORK_SPECS
spec :: Spec
spec = describe "Database.Orchestrate.Events" $ around_ (withFixtures fixtures) $ do
    describe "createEvent" $
        it "should create events." $
            withEvents' (fixtures !! 1) "create" events $ \locs -> do
                length (locs ^.. _Right . traverse) `shouldBe` 5
                locs ^.. _Right . traverse . locationKey  `shouldBe` replicate 5 "ella"
                locs ^.. _Right . traverse . locationType `shouldBe` replicate 5 "create"
                length (locs ^.. _Right . traverse . locationTimestamp) `shouldBe` 5
                length (locs ^.. _Right . traverse . locationOrdinal)   `shouldBe` 5

    describe "getEvent" $
        it "should retrieve the stored data for an event." $
            let p = head fixtures
            in  withEvents' p "get" events $ \(Right locs) -> do
                let events' = map fst events
                    items   = zip events'
                            $ zipWith locationEventItem locs events'
                forM_ items $ \(event, Just evtItem) -> do
                    e <- run $ getEvent p "get" (evtItem ^. eventTime) (evtItem ^. eventOrd)
                    e ^? _Right . _Just . eventItem . itemValue `shouldBe` Just event

    describe "updateEvent" $
        it "should update the data in the event." $
            let ev@(e, _) = head events
                p         = fixtures !! 1
            in  withEvents' p "life" [ev] $ \(Right [loc]) -> do
                let e' = e & eventScale .~ 3.1415
                r <- callLoc loc $ \t o -> updateEvent p "life" e' t o Nothing
                r `shouldSatisfy` isRight
                mNewE <- callLoc loc $ getEvent p "life"
                mNewE ^? _Right . _Just . eventItem . itemValue . eventScale
                    `shouldBe` Just 3.1415

    describe "deleteEvent" $
        it "should remove the event" $ do
            let (e, ts) = head events
                p       = fixtures !! 1
            Right loc <- run $ createEvent p "test" e (Just ts)
            r <- callLoc loc $ \t o -> deleteEvent p "test" t o Nothing
            r `shouldSatisfy` isRight
            Right e' <- (run $   eithererr loc (loc ^? locationOrdinal)
                             >>= getEvent p "test" ts) :: GetEvent
            e' `shouldSatisfy` isNothing

    describe "listEvents" $ around_ (withEvents (head fixtures) "list" events) $ do
        it "should return all events" $ do
            elist <- run $ listEvents (head fixtures) "list" Nothing (Open, Open)
            elist ^? _Right . resultCount `shouldBe` Just (length events)
            elist ^.. _Right . resultList . traverse . eventItem . itemValue . eventTitle
                `shouldMatchList` ["birth", "marriage", "child", "divorce", "death"]

#else
spec :: Spec
spec = describe "Database.Orchestrate.Events" $
    it "should contain tests." $
        pendingWith "configure with \"--enable-tests -fnetwork-specs\"."
#endif