]> git.immae.eu Git - github/fretlink/squeal-hspec.git/blobdiff - src/Squeal/PostgreSQL/Hspec.hs
bump to squeal 0.6
[github/fretlink/squeal-hspec.git] / src / Squeal / PostgreSQL / Hspec.hs
index 3cd886b0daf6f57425564cc8a1eaf0ff99e17e56..32ab18915ba3013032dd07a30903f4dffad0640a 100644 (file)
@@ -13,6 +13,7 @@ The libary also provides a few other functions for more fine grained control ove
 {-# LANGUAGE MonoLocalBinds   #-}
 {-# LANGUAGE RankNTypes       #-}
 {-# LANGUAGE RecordWildCards  #-}
+{-# LANGUAGE TupleSections    #-}
 {-# LANGUAGE TypeInType       #-}
 {-# LANGUAGE TypeOperators    #-}
 module Squeal.PostgreSQL.Hspec
@@ -21,33 +22,50 @@ where
 import           Control.Exception
 import           Control.Monad
 import           Control.Monad.Base     (liftBase)
+import           Data.ByteString        (ByteString)
 import qualified Data.ByteString.Char8  as BSC
 import qualified Database.Postgres.Temp as Temp
 import           Generics.SOP           (K)
 import           Squeal.PostgreSQL
-import           Squeal.PostgreSQL.Pool
+import           System.Environment     (lookupEnv)
 import           Test.Hspec
 
 data TestDB a = TestDB
-  { tempDB :: Temp.DB
+  { tempDB           :: Maybe Temp.DB
   -- ^ Handle for temporary @postgres@ process
-  , pool   :: Pool a
+  , pool             :: Pool (K Connection a)
   -- ^ Pool of 50 connections to the temporary @postgres@
+  , connectionString :: ByteString
   }
 
-type Fixtures schema = (Pool (K Connection schema) -> IO ())
-type Actions schema a = PoolPQ schema IO a
-type SquealContext schema = TestDB (K Connection schema)
+type Fixtures db a = (Pool (K Connection db) -> IO a)
+type Actions db a = PQ db db IO a
+type FixtureContext db fix = (TestDB db, fix)
+type Migrations def from to = Path (Migration def) from to
+
+testDBEnv :: String
+testDBEnv = "TEST_DB_CONNECTION_STRING"
+
+getOrCreateConnectionString :: IO (ByteString, Maybe Temp.DB)
+getOrCreateConnectionString = do
+  hasConnectionString <- lookupEnv testDBEnv
+  maybe createTempDB (pure . (, Nothing) . BSC.pack) hasConnectionString
+
+createTempDB :: IO (ByteString, Maybe Temp.DB)
+createTempDB = do
+  tempDB <- either throwIO return =<< Temp.startAndLogToTmp []
+  let connectionString = BSC.pack (Temp.connectionString tempDB)
+  pure (connectionString, Just tempDB)
 
 -- | Start a temporary @postgres@ process and create a pool of connections to it
 setupDB
-  :: AlignedList (Migration (Terminally PQ IO)) schema0 schema
-  -> Fixtures schema
-  -> IO (SquealContext schema)
+  :: Migratory def (IsoQ (Indexed PQ IO ()))
+  => Migrations def schema0 schema
+  -> Fixtures schema fix
+  -> IO (FixtureContext schema fix)
 setupDB migration fixtures = do
-  tempDB <- either throwIO return =<< Temp.startAndLogToTmp []
-  let connectionString = BSC.pack (Temp.connectionString tempDB)
-  putStrLn $ Temp.connectionString tempDB
+  (connectionString, tempDB) <- getOrCreateConnectionString
+  BSC.putStrLn connectionString
   let singleStripe = 1
       keepConnectionForOneHour = 3600
       poolSizeOfFifty = 50
@@ -57,32 +75,54 @@ setupDB migration fixtures = do
      keepConnectionForOneHour
      poolSizeOfFifty
   withConnection connectionString (migrateUp migration)
-  fixtures pool
-  pure TestDB {..}
+  res <- fixtures pool
+  pure (TestDB {..}, res)
 
 -- | Drop all the connections and shutdown the @postgres@ process
-teardownDB :: TestDB a -> IO ()
-teardownDB TestDB {..} = do
-  destroyAllResources pool
-  void $ Temp.stop tempDB
+teardownDB
+  :: Migratory def (IsoQ (Indexed PQ IO ()))
+  => Migrations def schema0 schema
+  -> TestDB a
+  -> IO ()
+teardownDB migration TestDB {..} = do
+  withConnection connectionString (migrateDown migration)
+  destroyConnectionPool pool
+  maybe (pure ()) (void . Temp.stop) tempDB
 
 -- | Run an 'IO' action with a connection from the pool
-withPool :: TestDB (K Connection schema) -> Actions schema a -> IO a
-withPool testDB = liftBase . flip runPoolPQ (pool testDB)
+withPool :: TestDB db -> Actions db a -> IO a
+withPool testDB = liftBase . usingConnectionPool (pool testDB)
 
 -- | Run an 'DB' transaction, using 'transactionally_'
-withDB :: Actions schema a -> TestDB (K Connection schema) -> IO a
+withDB :: Actions db a -> TestDB db -> IO a
 withDB action testDB =
-  runPoolPQ (transactionally_ action) (pool testDB)
+  usingConnectionPool (pool testDB) (transactionally_ action)
 
 -- | Flipped version of 'withDB'
-runDB :: TestDB (K Connection schema) -> Actions schema a -> IO a
+runDB :: TestDB db -> Actions db a -> IO a
 runDB = flip withDB
 
+withFixture :: (fix -> Actions db a) -> FixtureContext db fix -> IO a
+withFixture action (db, fix) =
+  usingConnectionPool (pool db) (transactionally_ $ action fix)
+
+withoutFixture :: Actions db a -> FixtureContext db fix -> IO a
+withoutFixture action (db, _) =
+  usingConnectionPool (pool db) (transactionally_ action)
+
 -- | Helper for writing tests. Wrapper around 'it' that uses the passed
 --   in 'TestDB' to run a db transaction automatically for the test.
-itDB :: String -> Actions schema a -> SpecWith (TestDB (K Connection schema))
-itDB msg action = it msg $ void . withDB action
+itDB :: String -> Actions db a -> SpecWith (FixtureContext db ())
+itDB msg action = it msg $ void . withoutFixture action
+
+-- | Helper for writing tests. Wrapper around 'it' that uses the passed
+-- in 'TestDB' to run a db transaction automatically for the test,
+-- plus the result of the fixtures.
+itDBF :: String -> (fix -> Actions db a) -> SpecWith (FixtureContext db fix)
+itDBF msg action = it msg $ void . withFixture action
+
+itDBF_ :: String -> Actions db a -> SpecWith (FixtureContext db fix)
+itDBF_ msg action = it msg $ void . withoutFixture action
 
 -- | Wraps 'describe' with a
 --
@@ -98,10 +138,23 @@ itDB msg action = it msg $ void . withDB action
 --
 -- hook for stopping a db.
 describeDB
-  :: AlignedList (Migration (Terminally PQ IO)) schema0 schema
-  -> Fixtures schema
+  :: Migratory def (IsoQ (Indexed PQ IO ()))
+  => Migrations def db0 db
+  -> Fixtures db ()
   -> String
-  -> SpecWith (SquealContext schema)
+  -> SpecWith (FixtureContext db ())
   -> Spec
 describeDB migrate fixture str =
-  beforeAll (setupDB migrate fixture) . afterAll teardownDB . describe str
+  beforeAll (setupDB migrate fixture) . afterAll (teardownDB migrate . fst) . describe str
+
+-- | Like `decribeDB`, but allow fixtures to pass
+-- | a result to all specs
+describeFixtures
+  :: Migratory def (IsoQ (Indexed PQ IO ()))
+  => Migrations def db0 db
+  -> Fixtures db fix
+  -> String
+  -> SpecWith (FixtureContext db fix)
+  -> Spec
+describeFixtures migrate fixture str =
+  beforeAll (setupDB migrate fixture) . afterAll (teardownDB migrate . fst) . describe str