packages feed

hasql-queue-1.2.0.0: test/Hasql/Queue/TestUtils.hs

module Hasql.Queue.TestUtils where
import qualified Data.ByteString.Char8 as BSC
import Control.Concurrent.Async
import           Database.Postgres.Temp as Temp
import           Test.Hspec
import           Control.Exception as E
import           Data.Pool
import           Hasql.Connection
import           Hasql.Session
import qualified Data.ByteString.Base64.URL as Base64
import           Control.Concurrent
import           Data.IORef
import           Data.Foldable
import           Control.Monad ((<=<))
import           Crypto.Hash.SHA1 (hash)
import           Hasql.Queue.Migrate

aroundAll :: forall a. ((a -> IO ()) -> IO ()) -> SpecWith a -> Spec
aroundAll withFunc specWith = do
  (var, stopper, asyncer) <- runIO $
    (,,) <$> newEmptyMVar <*> newEmptyMVar <*> newIORef Nothing
  let theStart :: IO a
      theStart = do

        thread <- async $ do
          withFunc $ \x -> do
            putMVar var x
            takeMVar stopper
          pure $ error "Don't evaluate this"

        writeIORef asyncer $ Just thread

        either pure pure =<< (wait thread `race` takeMVar var)

      theStop :: a -> IO ()
      theStop _ = do
        putMVar stopper ()
        traverse_ cancel =<< readIORef asyncer

  beforeAll theStart $ afterAll theStop $ specWith

withConn :: Temp.DB -> (Connection -> IO a) -> IO a
withConn db f = do
  let connStr = toConnectionString db
  E.bracket (either (throwIO . userError . show) pure =<< acquire connStr) release f

withSetup :: (Pool Connection -> IO ()) -> IO ()
withSetup f = either throwIO pure <=< withDbCache $ \dbCache -> do
  migratedConfig <- either throwIO pure =<<
      cacheAction
        (("~/.tmp-postgres/" <>) . BSC.unpack . Base64.encode . hash
          $ BSC.pack $ migrationQueryString "int4")
        (flip withConn $ flip migrate "int4")
        (verboseConfig <> cacheConfig dbCache)
  withConfig migratedConfig $ \db -> do
    f =<< createPool
      (either (throwIO . userError . show) pure =<< acquire (toConnectionString db))
      release
      2
      60
      50

withConnection :: (Connection -> IO ()) -> Pool Connection -> IO ()
withConnection = flip withResource

withConnection2 :: ((Connection, Connection) -> IO ()) -> Pool Connection -> IO ()
withConnection2 f pool = withResource pool $ \conn1 ->
   withResource pool $ \conn2 -> f (conn1, conn2)

runImplicitTransaction :: Pool Connection -> Session a -> IO a
runImplicitTransaction pool action = do
  let wrappedAction = do
        r <- action
        pure r
  withResource pool $ \conn ->
    either (throwIO . userError . show) pure =<< run wrappedAction conn

runReadCommitted :: Pool Connection -> Session a -> IO a
runReadCommitted = flip withReadCommitted

withReadCommitted :: Session a -> Pool Connection -> IO a
withReadCommitted action pool = do
  let wrappedAction = do
        sql "BEGIN"
        r <- action
        sql "ROLLBACK"
        pure r
  withResource pool $ \conn ->
    either (throwIO . userError . show) pure =<< run wrappedAction conn