]> git.immae.eu Git - github/fretlink/squeal-hspec.git/blame_incremental - src/Squeal/PostgreSQL/Hspec.hs
Support fixed databases
[github/fretlink/squeal-hspec.git] / src / Squeal / PostgreSQL / Hspec.hs
... / ...
CommitLineData
1{-|
2Helpers for creating database tests with hspec and squeal, inspired by Jonathan Fischoff's
3[hspec-pg-transact](http://hackage.haskell.org/package/hspec-pg-transact).
4
5This uses @tmp-postgres@ to automatically and connect to a temporary instance of postgres on a random port.
6
7Tests can be written with 'itDB' which is wrapper around 'it' that uses the passed in 'TestDB' to run a db transaction automatically for the test.
8
9The libary also provides a few other functions for more fine grained control over running transactions in tests.
10-}
11{-# LANGUAGE DataKinds #-}
12{-# LANGUAGE FlexibleContexts #-}
13{-# LANGUAGE MonoLocalBinds #-}
14{-# LANGUAGE RankNTypes #-}
15{-# LANGUAGE RecordWildCards #-}
16{-# LANGUAGE TupleSections #-}
17{-# LANGUAGE TypeInType #-}
18{-# LANGUAGE TypeOperators #-}
19module Squeal.PostgreSQL.Hspec
20where
21
22import Control.Exception
23import Control.Monad
24import Control.Monad.Base (liftBase)
25import Data.ByteString (ByteString)
26import qualified Data.ByteString.Char8 as BSC
27import qualified Database.Postgres.Temp as Temp
28import Generics.SOP (K)
29import Squeal.PostgreSQL
30import Squeal.PostgreSQL.Pool
31import System.Environment (lookupEnv)
32import Test.Hspec
33
34data TestDB a = TestDB
35 { tempDB :: Maybe Temp.DB
36 -- ^ Handle for temporary @postgres@ process
37 , pool :: Pool a
38 -- ^ Pool of 50 connections to the temporary @postgres@
39 , connectionString :: ByteString
40 }
41
42type Fixtures schema = (Pool (K Connection schema) -> IO ())
43type Actions schema a = PoolPQ schema IO a
44type SquealContext schema = TestDB (K Connection schema)
45
46testDBEnv :: String
47testDBEnv = "TEST_DB_CONNECTION_STRING"
48
49getOrCreateConnectionString :: IO (ByteString, Maybe Temp.DB)
50getOrCreateConnectionString = do
51 hasConnectionString <- lookupEnv testDBEnv
52 maybe createTempDB (pure . (, Nothing) . BSC.pack) hasConnectionString
53
54createTempDB :: IO (ByteString, Maybe Temp.DB)
55createTempDB = do
56 tempDB <- either throwIO return =<< Temp.startAndLogToTmp []
57 let connectionString = BSC.pack (Temp.connectionString tempDB)
58 pure (connectionString, Just tempDB)
59
60-- | Start a temporary @postgres@ process and create a pool of connections to it
61setupDB
62 :: AlignedList (Migration (Terminally PQ IO)) schema0 schema
63 -> Fixtures schema
64 -> IO (SquealContext schema)
65setupDB migration fixtures = do
66 (connectionString, tempDB) <- getOrCreateConnectionString
67 BSC.putStrLn connectionString
68 let singleStripe = 1
69 keepConnectionForOneHour = 3600
70 poolSizeOfFifty = 50
71 pool <- createConnectionPool
72 connectionString
73 singleStripe
74 keepConnectionForOneHour
75 poolSizeOfFifty
76 withConnection connectionString (migrateUp migration)
77 fixtures pool
78 pure TestDB {..}
79
80-- | Drop all the connections and shutdown the @postgres@ process
81teardownDB
82 :: AlignedList (Migration (Terminally PQ IO)) schema0 schema
83 -> TestDB a
84 -> IO ()
85teardownDB migration TestDB {..} = do
86 withConnection connectionString (migrateDown migration)
87 destroyAllResources pool
88 maybe (pure ()) (void . Temp.stop) tempDB
89
90-- | Run an 'IO' action with a connection from the pool
91withPool :: TestDB (K Connection schema) -> Actions schema a -> IO a
92withPool testDB = liftBase . flip runPoolPQ (pool testDB)
93
94-- | Run an 'DB' transaction, using 'transactionally_'
95withDB :: Actions schema a -> TestDB (K Connection schema) -> IO a
96withDB action testDB =
97 runPoolPQ (transactionally_ action) (pool testDB)
98
99-- | Flipped version of 'withDB'
100runDB :: TestDB (K Connection schema) -> Actions schema a -> IO a
101runDB = flip withDB
102
103-- | Helper for writing tests. Wrapper around 'it' that uses the passed
104-- in 'TestDB' to run a db transaction automatically for the test.
105itDB :: String -> Actions schema a -> SpecWith (TestDB (K Connection schema))
106itDB msg action = it msg $ void . withDB action
107
108-- | Wraps 'describe' with a
109--
110-- @
111-- 'beforeAll' ('setupDB' migrate)
112-- @
113--
114-- hook for creating a db and a
115--
116-- @
117-- 'afterAll' 'teardownDB'
118-- @
119--
120-- hook for stopping a db.
121describeDB
122 :: AlignedList (Migration (Terminally PQ IO)) schema0 schema
123 -> Fixtures schema
124 -> String
125 -> SpecWith (SquealContext schema)
126 -> Spec
127describeDB migrate fixture str =
128 beforeAll (setupDB migrate fixture) . afterAll (teardownDB migrate) . describe str