module Database.Seakale.Tests.Request ( RequestMock , mockMatchingQuery , mockMatchingExecute , mockQuery , mockExecute , runRequestT , runRequest , runRequestT' , runRequest' , module Database.Seakale.Tests.Mock ) where import Control.Monad.Identity import Control.Monad.State import Control.Monad.Trans.Free import qualified Control.Monad.Except as E import Data.Monoid import qualified Data.ByteString.Lazy as BSL import Database.Seakale.Request.Internal hiding (runRequestT, runRequest) import Database.Seakale.Types hiding (runQuery, runExecute) import Database.Seakale.Tests.Mock data RequestMock backend = MockQuery QueryPredicate ([ColumnInfo backend], [Row backend]) | MockExecute QueryPredicate Integer mockMatchingQuery :: (BSL.ByteString -> Bool) -> ([ColumnInfo backend], [Row backend]) -> Mock (RequestMock backend) () mockMatchingQuery f dat = Action $ MockQuery (QPredFunction f) dat mockMatchingExecute :: (BSL.ByteString -> Bool) -> Integer -> Mock (RequestMock backend) () mockMatchingExecute f i = Action $ MockExecute (QPredFunction f) i mockQuery :: BSL.ByteString -> ([ColumnInfo backend], [Row backend]) -> Mock (RequestMock backend) () mockQuery req dat = Action $ MockQuery (QPredPlain req) dat mockExecute :: BSL.ByteString -> Integer -> Mock (RequestMock backend) () mockExecute req i = Action $ MockExecute (QPredPlain req) i runQuery :: BSL.ByteString -> Mock (RequestMock backend) a -> Maybe ( ([ColumnInfo backend], [Row backend]) , Mock (RequestMock backend) a ) runQuery req = consumeMock $ \case MockQuery p cr | runQueryPredicate p req -> Just cr _ -> Nothing runExecute :: BSL.ByteString -> Mock (RequestMock backend) a -> Maybe (Integer, Mock (RequestMock backend) a) runExecute req = consumeMock $ \case MockExecute p i | runQueryPredicate p req -> Just i _ -> Nothing runRequestT :: Monad m => backend -> Mock (RequestMock backend) b -> RequestT backend m a -> m (Either SeakaleError a) runRequestT b m = fmap fst . runRequestT' b m runRequest :: backend -> Mock (RequestMock backend) b -> Request backend a -> Either SeakaleError a runRequest b m = fst . runRequest' b m runRequestT' :: Monad m => backend -> Mock (RequestMock backend) b -> RequestT backend m a -> m (Either SeakaleError a, Mock (RequestMock backend) b) runRequestT' b m = flip runStateT m . E.runExceptT . iterT (interpreter b) . hoistFreeT (lift . lift) where interpreter :: Monad m => backend -> RequestF backend (E.ExceptT SeakaleError (StateT (Mock (RequestMock backend) b) m) a) -> E.ExceptT SeakaleError (StateT (Mock (RequestMock backend) b) m) a interpreter backend = \case Query req f -> do mock <- get case runQuery req mock of Nothing -> E.throwError $ BackendError $ "no mock found for Query on " <> BSL.toStrict req Just (res, mock') -> put mock' >> f res Execute req f -> do mock <- get case runExecute req mock of Nothing -> E.throwError $ BackendError $ "no mock found for Execute on " <> BSL.toStrict req Just (res, mock') -> put mock' >> f res GetBackend f -> f backend ThrowError err -> E.throwError err CatchError action handler -> E.catchError action handler runRequest' :: backend -> Mock (RequestMock backend) b -> Request backend a -> (Either SeakaleError a, Mock (RequestMock backend) b) runRequest' backend mock = runIdentity . runRequestT' backend mock